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 :
Lecture des cookies
Vous pouvez lire les cookies dans une Route API en utilisant l'assistant de requête cookies
:
Suppression des cookies
Vous pouvez supprimer des cookies dans une Route API en utilisant la méthode setHeader
sur la réponse :