backend / graphql
Mutations : écrire des données
Explication
Ce que vous allez apprendre
- Écrire une mutation qui modifie des données et récupère le résultat en une seule requête
- Utiliser des variables plutôt que des valeurs en dur dans une mutation
- Comprendre pourquoi plusieurs mutations dans une même requête s'exécutent séquentiellement
- Concevoir un type de retour "payload" qui distingue succès et erreurs métier
- Éviter de confondre une erreur métier (validation) et une erreur technique GraphQL
Dans quel contexte ?
Un formulaire "Ajouter un produit" dans un back-office e-commerce envoie les données saisies (nom, prix, catégorie) au serveur. Le développeur frontend a besoin de deux choses en une seule requête : que le produit soit créé en base, et que l'identifiant généré par le serveur revienne immédiatement pour rediriger l'utilisateur vers la fiche du nouveau produit. C'est exactement le rôle d'une mutation GraphQL.
D'abord, une mutation ressemble beaucoup à une query
La syntaxe est presque identique : on utilise le mot-clé mutation à la place de query, puis on sélectionne les champs qu'on veut récupérer sur le résultat. La différence essentielle n'est pas syntaxique mais sémantique : un champ de Mutation a le droit de modifier l'état du serveur, un champ de Query ne le devrait jamais.
Une fois cette syntaxe posée, pourquoi toujours passer par des variables ?
Écrire creerProduit(nom: "Clavier", prix: 89.90) fonctionne pour un test rapide dans GraphiQL, mais en production, les valeurs viennent de l'utilisateur et changent à chaque appel. Les variables ($nom, $prix) séparent le texte fixe de la requête (mis en cache, réutilisé) des données variables (envoyées séparément dans le champ variables du corps JSON) — exactement comme une requête SQL préparée sépare le texte de ses paramètres.
Il reste une question importante : que se passe-t-il avec plusieurs mutations dans une requête ?
Contrairement aux queries, dont les champs peuvent être résolus en parallèle par le serveur, les mutations d'une même requête s'exécutent toujours dans l'ordre d'écriture, une par une. C'est un choix de conception délibéré de la spécification GraphQL : si une mutation décrémente un stock et qu'une autre l'utilise pour calculer des ventes, l'ordre d'exécution doit être prévisible.
Ensuite, une question de conception se pose : comment gérer les erreurs métier ?
Une validation qui échoue (prix négatif, nom vide) n'est pas la même chose qu'une panne serveur ou un token invalide. Le pattern "payload", très répandu dans les schémas GraphQL professionnels, consiste à faire retourner à chaque mutation un objet avec deux champs : le résultat (produit, potentiellement null) et une liste d'erreurs métier explicites (erreurs).
| Type d'erreur | Où elle apparaît | Exemple |
|---|---|---|
| Erreur métier (validation) | Dans les données, via le pattern payload | { champ: "prix", message: "doit être positif" } |
| Erreur technique GraphQL | Dans le tableau errors de la réponse | Authentification manquante, syntaxe invalide |
Bonne pratique
Réserve le tableau errors (standard GraphQL, détaillé dans la leçon dédiée à la gestion des erreurs) aux vraies erreurs techniques — authentification, autorisation, erreur serveur. Pour une validation métier attendue (un prix négatif saisi par erreur), préfère le pattern payload : le client peut alors afficher un message précis à côté du bon champ de formulaire, sans avoir à parser un message d'erreur générique.
Piège fréquent
Interpoler directement des valeurs utilisateur dans le texte d'une requête (creerProduit(nom: " + saisieUtilisateur + ")) au lieu d'utiliser des variables expose à des requêtes cassées, voire à des comportements inattendus si la valeur contient des guillemets. Les variables typées sont validées par le serveur avant même d'atteindre le resolver : utilise-les systématiquement.
Maintenant que tu sais lire et écrire des données côté client, il est temps de voir comment le serveur répond réellement à ces requêtes : la prochaine leçon plonge dans les resolvers, le code qui exécute chaque champ du schéma.
Commandes & code
Mutations : écrire des données
# Une mutation retourne toujours les données résultantes -- le client sait quoi rafraîchir
mutation CreerProduit {
creerProduit(nom: "Clavier mécanique", prix: 89.90) {
id
nom
prix
}
}
# Mutations avec variables (bonne pratique : jamais de valeurs en dur dans la requête)
mutation CreerProduit($nom: String!, $prix: Float!, $categorieId: ID!) {
creerProduit(input: { nom: $nom, prix: $prix, categorieId: $categorieId }) {
id
nom
categorie { nom }
}
}// Variables envoyées séparément de la requête (dans le corps JSON de la requête HTTP)
{
"query": "mutation CreerProduit($nom: String!, $prix: Float!) { creerProduit(nom: $nom, prix: $prix) { id nom } }",
"variables": { "nom": "Souris sans fil", "prix": 24.90 }
}# Plusieurs mutations dans une même requête : exécutées SÉQUENTIELLEMENT (pas en parallèle)
mutation MettreAJourStock {
decrementerStock(produitId: "42", quantite: 3) {
id
quantite
}
incrementerVentes(produitId: "42") {
totalVentes
}
}
# Pattern "payload" : type de retour uniforme avec succès/erreurs métier explicites
type CreerProduitPayload {
produit: Produit
erreurs: [ErreurValidation!]
}
type ErreurValidation {
champ: String!
message: String!
}
type Mutation {
creerProduit(input: CreerProduitInput!): CreerProduitPayload!
}# Utilisation côté client du pattern payload : distinguer succès et échec métier
mutation {
creerProduit(input: { nom: "", prix: -5 }) {
produit { id nom }
erreurs {
champ
message
}
}
}// Réponse : la mutation "réussit" au sens HTTP/GraphQL, mais porte des erreurs métier explicites
{
"data": {
"creerProduit": {
"produit": null,
"erreurs": [
{ "champ": "nom", "message": "Le nom ne peut pas être vide" },
{ "champ": "prix", "message": "Le prix doit être positif" }
]
}
}
}Résumé
- Une mutation retourne les données modifiées pour permettre au client cache/UI de se mettre à jour.
- Plusieurs mutations dans une requête s'exécutent dans l'ordre, jamais en parallèle (contrairement aux queries).
- Le pattern payload (
{ produit, erreurs }) distingue les erreurs métier (validation) des erreurs GraphQL techniques. - Toujours utiliser des variables plutôt que d'interpoler des valeurs directement dans la chaîne de requête.
Exercices pratiques
Mission : concevoir une mutation de création de produit robuste pour un back-office
Objectif : Remplacer un tableau errors mal utilisé par le pattern payload pour distinguer erreurs métier et erreurs techniques, avec des variables typées.
Contexte
Le formulaire "Ajouter un produit" du back-office envoie un prix négatif par erreur de saisie. Aujourd'hui, la mutation lève une exception générique qui atterrit dans le tableau errors standard de GraphQL, mélangée avec de vraies pannes serveur. Le product manager veut que le formulaire affiche un message précis à côté du champ prix, sans avoir à parser un texte libre.