Formulaires et mutations

Les formulaires vous permettent de créer et mettre à jour des données dans les applications web. Next.js offre une solution puissante pour gérer les soumissions de formulaires et les mutations de données en utilisant les Routes API.

Bon à savoir :

  • Nous recommanderons bientôt d'adopter progressivement le routeur App et d'utiliser les Actions Serveur pour gérer les soumissions de formulaires et les mutations de données. Les Actions Serveur vous permettent de définir des fonctions serveur asynchrones qui peuvent être appelées directement depuis vos composants, sans avoir à créer manuellement une Route API.
  • Les Routes API ne spécifient pas d'en-têtes CORS, ce qui signifie qu'elles sont par défaut limitées à la même origine.
  • Comme les Routes API s'exécutent sur le serveur, nous pouvons utiliser des valeurs sensibles (comme des clés API) via les Variables d'Environnement sans les exposer au client. Ceci est crucial pour la sécurité de votre application.

Exemples

Formulaires Serveur Uniquement

Avec le routeur Pages, vous devez créer manuellement des points d'API pour gérer de manière sécurisée les mutations de données sur le serveur.

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const data = req.body
  const id = await createItem(data)
  res.status(200).json({ id })
}
export default function handler(req, res) {
  const data = req.body
  const id = await createItem(data)
  res.status(200).json({ id })
}

Ensuite, appelez la Route API depuis le client avec un gestionnaire d'événements :

import { FormEvent } from 'react'

export default function Page() {
  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const formData = new FormData(event.currentTarget)
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData,
    })

    // Gérer la réponse si nécessaire
    const data = await response.json()
    // ...
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit">Soumettre</button>
    </form>
  )
}
export default function Page() {
  async function onSubmit(event) {
    event.preventDefault()

    const formData = new FormData(event.target)
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData,
    })

    // Gérer la réponse si nécessaire
    const data = await response.json()
    // ...
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit">Soumettre</button>
    </form>
  )
}

Redirection

Si vous souhaitez rediriger l'utilisateur vers une route différente après une mutation, vous pouvez utiliser redirect vers n'importe quelle URL absolue ou relative :

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const id = await addPost()
  res.redirect(307, `/post/${id}`)
}
export default async function handler(req, res) {
  const id = await addPost()
  res.redirect(307, `/post/${id}`)
}

Validation de Formulaire

Nous recommandons d'utiliser la validation HTML comme required et type="email" pour une validation de formulaire basique.

Pour une validation côté serveur plus avancée, utilisez une bibliothèque de validation de schéma comme zod pour valider la structure des données de formulaire analysées :

import type { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'

const schema = z.object({
  // ...
})

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const parsed = schema.parse(req.body)
  // ...
}
import { z } from 'zod'

const schema = z.object({
  // ...
})

export default async function handler(req, res) {
  const parsed = schema.parse(req.body)
  // ...
}

Affichage de l'État de Chargement

Vous pouvez utiliser l'état React pour afficher un état de chargement lorsqu'un formulaire est en cours de soumission sur le serveur :

import React, { useState, FormEvent } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState<boolean>(false)

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setIsLoading(true) // Définir le chargement à true lorsque la requête commence

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      // Gérer la réponse si nécessaire
      const data = await response.json()
      // ...
    } catch (error) {
      // Gérer l'erreur si nécessaire
      console.error(error)
    } finally {
      setIsLoading(false) // Définir le chargement à false lorsque la requête se termine
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Chargement...' : 'Soumettre'}
      </button>
    </form>
  )
}
import React, { useState } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState(false)

  async function onSubmit(event) {
    event.preventDefault()
    setIsLoading(true) // Définir le chargement à true lorsque la requête commence

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      // Gérer la réponse si nécessaire
      const data = await response.json()
      // ...
    } catch (error) {
      // Gérer l'erreur si nécessaire
      console.error(error)
    } finally {
      setIsLoading(false) // Définir le chargement à false lorsque la requête se termine
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Chargement...' : 'Soumettre'}
      </button>
    </form>
  )
}

Gestion des erreurs

Vous pouvez utiliser l'état React pour afficher un message d'erreur lorsqu'une soumission de formulaire échoue :

import React, { useState, FormEvent } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState<boolean>(false)
  const [error, setError] = useState<string | null>(null)

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setIsLoading(true)
    setError(null) // Effacer les erreurs précédentes lorsqu'une nouvelle requête démarre

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      if (!response.ok) {
        throw new Error('Échec de la soumission des données. Veuillez réessayer.')
      }

      // Traiter la réponse si nécessaire
      const data = await response.json()
      // ...
    } catch (error) {
      // Capturer le message d'erreur à afficher à l'utilisateur
      setError(error.message)
      console.error(error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div>
      {error && <div style={{ color: 'red' }}>{error}</div>}
      <form onSubmit={onSubmit}>
        <input type="text" name="name" />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Chargement...' : 'Soumettre'}
        </button>
      </form>
    </div>
  )
}
import React, { useState } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState(null)

  async function onSubmit(event) {
    event.preventDefault()
    setIsLoading(true)
    setError(null) // Effacer les erreurs précédentes lorsqu'une nouvelle requête démarre

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      if (!response.ok) {
        throw new Error('Échec de la soumission des données. Veuillez réessayer.')
      }

      // Traiter la réponse si nécessaire
      const data = await response.json()
      // ...
    } catch (error) {
      // Capturer le message d'erreur à afficher à l'utilisateur
      setError(error.message)
      console.error(error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div>
      {error && <div style={{ color: 'red' }}>{error}</div>}
      <form onSubmit={onSubmit}>
        <input type="text" name="name" />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Chargement...' : 'Soumettre'}
        </button>
      </form>
    </div>
  )
}

Définition des cookies

Vous pouvez définir des cookies dans une Route API en utilisant la méthode setHeader sur la réponse :

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.setHeader('Set-Cookie', 'username=lee; Path=/; HttpOnly')
  res.status(200).send('Le cookie a été défini.')
}
export default async function handler(req, res) {
  res.setHeader('Set-Cookie', 'username=lee; Path=/; HttpOnly')
  res.status(200).send('Le cookie a été défini.')
}

Lecture des cookies

Vous pouvez lire les cookies dans une Route API en utilisant l'assistant de requête cookies :

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const auth = req.cookies.authorization
  // ...
}
export default async function handler(req, res) {
  const auth = req.cookies.authorization
  // ...
}

Suppression des cookies

Vous pouvez supprimer des cookies dans une Route API en utilisant la méthode setHeader sur la réponse :

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.setHeader('Set-Cookie', 'username=; Path=/; HttpOnly; Max-Age=0')
  res.status(200).send('Le cookie a été supprimé.')
}
export default async function handler(req, res) {
  res.setHeader('Set-Cookie', 'username=; Path=/; HttpOnly; Max-Age=0')
  res.status(200).send('Le cookie a été supprimé.')
}