Découvrez ce qu'est GraphQL, comment fonctionnent les queries, mutations et schémas, et quand l'utiliser plutôt que REST — avec les avantages, inconvénients et exemples pratiques.

GraphQL est un langage de requête et un runtime pour les API. En bref : c'est un moyen pour une application (web, mobile ou un autre service) de demander des données à une API via une requête claire et structurée — et pour le serveur de renvoyer une réponse qui correspond à cette requête.
Beaucoup d'API obligent les clients à accepter ce qu'un endpoint fixe retourne. Cela mène souvent à deux problèmes :
Avec GraphQL, le client peut demander exactement les champs dont il a besoin, ni plus ni moins. C'est particulièrement utile quand différents écrans (ou différentes applis) ont besoin de « tranches » différentes des mêmes données sous-jacentes.
GraphQL se place généralement entre les applications clientes et vos sources de données. Ces sources peuvent être :
Le serveur GraphQL reçoit une requête, détermine comment récupérer chaque champ demandé depuis la bonne source, puis assemble la réponse JSON finale.
Considérez GraphQL comme commander une réponse sur mesure :
GraphQL est souvent mal compris, donc quelques clarifications :
Si vous retenez cette définition de base — langage de requête + runtime pour les API — vous aurez la bonne fondation pour la suite.
GraphQL a été créé pour résoudre un problème produit concret : les équipes passaient trop de temps à adapter les APIs aux écrans UI.
Les APIs traditionnelles basées sur des endpoints forcent souvent un choix entre envoyer des données inutiles ou faire des appels supplémentaires pour obtenir ce dont on a besoin. Avec la croissance des produits, cette friction se traduit par des pages plus lentes, un code client plus complexe et une coordination pénible entre frontend et backend.
La sur-récupération survient quand un endpoint retourne un objet « complet » alors qu'un écran n'a besoin que de quelques champs. Une vue de profil mobile peut n'avoir besoin que d'un nom et d'un avatar, mais l'API renvoie adresses, préférences, champs d'audit, etc. Cela gaspille de la bande passante et peut nuire à l'expérience.
La sous-récupération est l'inverse : aucun endpoint unique ne contient tout ce dont une vue a besoin, le client doit donc faire plusieurs requêtes et assembler les résultats. Cela ajoute de la latence et augmente le risque d'erreurs partielles.
Beaucoup d'APIs REST réagissent au changement en ajoutant des endpoints ou des versions (v1, v2, v3). Le versioning peut être nécessaire, mais il crée une charge de maintenance : les vieux clients continuent d'utiliser d'anciennes versions tandis que les nouvelles fonctionnalités s'empilent ailleurs.
L'approche de GraphQL consiste à faire évoluer le schéma en ajoutant champs et types au fil du temps, tout en maintenant la stabilité des champs existants. Cela réduit souvent la pression de créer de « nouvelles versions » juste pour supporter de nouveaux besoins UI.
Les produits modernes ont rarement un unique consommateur. Web, iOS, Android et les intégrations partenaires ont tous besoin de formes de données différentes.
GraphQL a été conçu pour que chaque client puisse demander exactement les champs dont il a besoin — sans que le backend crée un endpoint séparé pour chaque écran ou appareil.
Une API GraphQL est définie par son schéma. Pensez-y comme l'accord entre le serveur et tous les clients : il liste quelles données existent, comment elles sont connectées et ce qu'il est possible de demander ou de modifier. Les clients ne devinent pas les endpoints — ils lisent le schéma et demandent des champs précis.
Le schéma est composé de types (comme User ou Post) et de champs (comme name ou title). Les champs peuvent pointer vers d'autres types, c'est ainsi que GraphQL modélise les relations.
Voici un exemple simple en SDL (Schema Definition Language) :
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String
author: User!
comments: [Comment!]!
}
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}
Parce que le schéma est fortement typé, GraphQL peut valider une requête avant de l'exécuter. Si un client demande un champ qui n'existe pas (par ex. Post.publishDate alors que le schéma n'a pas ce champ), le serveur peut refuser ou partiellement satisfaire la requête avec des erreurs claires — sans comportement ambigu.
Les schémas sont pensés pour grandir. Vous pouvez généralement ajouter de nouveaux champs (par ex. User.bio) sans casser les clients existants, parce que les clients ne reçoivent que ce qu'ils demandent. Supprimer ou modifier des champs est plus délicat : les équipes déprécient souvent d'abord les champs et migrent les clients progressivement.
Une API GraphQL est typiquement exposée via un endpoint unique (par exemple /graphql). Plutôt que d'avoir de nombreuses URLs pour différentes ressources (comme /users, /users/123, /users/123/posts), vous envoyez une query en un seul point et décrivez les données exactes que vous voulez.
Une requête est essentiellement une « liste de courses » de champs. Vous pouvez demander des champs simples (comme id et name) et aussi des données imbriquées (comme les posts récents d'un utilisateur) dans la même requête — sans télécharger des champs supplémentaires inutiles.
Voici un petit exemple :
query GetUserWithPosts {
user(id: "123") {
id
name
posts(limit: 2) {
id
title
}
}
}
Les réponses GraphQL sont prévisibles : le JSON renvoyé reflète la structure de votre requête. Cela facilite le travail côté frontend, car vous n'avez pas à deviner où apparaîtront les données ni à parser des formats différents.
Un exemple simplifié de réponse pourrait ressembler à :
{
"data": {
"user": {
"id": "123",
"name": "Sam",
"posts": [
{ "id": "p1", "title": "Hello GraphQL" },
{ "id": "p2", "title": "Queries in Practice" }
]
}
}
}
Si vous ne demandez pas un champ, il ne sera pas inclus. Si vous le demandez, vous pouvez vous attendre à le trouver à l'emplacement correspondant — ce qui fait des requêtes GraphQL un moyen propre de récupérer exactement ce dont chaque écran ou fonctionnalité a besoin.
Les queries servent à lire ; les mutations servent à modifier des données dans une API GraphQL — créer, mettre à jour ou supprimer des enregistrements.
La plupart des mutations suivent le même schéma :
input) contenant les champs à modifier.Les mutations GraphQL retournent généralement des données à dessein, plutôt que simplement "success: true". Retourner l'objet mis à jour (ou au moins son id et ses champs clés) aide l'UI à :
Un design courant est un type de "payload" qui inclut à la fois l'entité mise à jour et d'éventuelles erreurs.
mutation UpdateEmail($input: UpdateUserEmailInput!) {
updateUserEmail(input: $input) {
user {
id
email
}
errors {
field
message
}
}
}
Pour des APIs orientées UI, une bonne règle est : retourner ce dont vous avez besoin pour rendre l'état suivant (par ex. l'user mis à jour plus les errors). Cela simplifie le client, évite de deviner ce qui a changé et rend les échecs plus faciles à gérer.
Un schéma GraphQL décrit ce qu'on peut demander. Les résolveurs décrivent comment l'obtenir réellement. Un résolveur est une fonction attachée à un champ spécifique du schéma. Quand un client demande ce champ, GraphQL appelle le résolveur pour récupérer ou calculer la valeur.
GraphQL exécute une requête en parcourant la forme demandée. Pour chaque champ, il trouve le résolveur correspondant et l'exécute. Certains résolveurs retournent simplement une propriété d'un objet en mémoire ; d'autres appellent une base de données, un autre service, ou combinent plusieurs sources.
Par exemple, si votre schéma a User.posts, le résolveur posts peut interroger la table posts par userId, ou appeler un service Posts séparé.
Les résolveurs sont la colle entre le schéma et vos systèmes réels :
Ce mappage est flexible : vous pouvez changer l'implémentation backend sans modifier la forme des requêtes clients — tant que le schéma reste cohérent.
Parce que les résolveurs peuvent s'exécuter par champ et par élément d'une liste, il est facile de déclencher involontairement de nombreux petits appels (par ex. récupérer les posts pour 100 utilisateurs via 100 requêtes séparées). Ce pattern N+1 peut ralentir les réponses.
Les solutions courantes incluent le batching et le caching (par ex. regrouper les IDs et tout récupérer en une seule requête) et être intentionnel sur les champs imbriqués que vous incitez les clients à demander.
L'autorisation s'applique souvent dans les résolveurs (ou via un middleware partagé) car les résolveurs connaissent qui fait la requête (via le context) et quelles données sont demandées. La validation se fait à deux niveaux : GraphQL gère la validation de type/forme automatiquement, tandis que les résolveurs appliquent les règles métiers (par ex. "seuls les admins peuvent définir ce champ").
Une chose qui surprend les débutants est qu'une requête peut "réussir" tout en incluant des erreurs. C'est parce que GraphQL est orienté champ : si certains champs peuvent être résolus et d'autres non, vous pouvez obtenir des données partielles.
Une réponse GraphQL typique peut contenir à la fois data et un tableau errors :
{
"data": {
"user": {
"id": "123",
"email": null
}
},
"errors": [
{
"message": "Not authorized to read email",
"path": ["user", "email"],
"extensions": { "code": "FORBIDDEN" }
}
]
}
C'est utile : le client peut toujours afficher ce qu'il a (par ex. le profil utilisateur) tout en gérant le champ manquant.
data est souvent null.Rédigez des messages d'erreur pour l'utilisateur final, pas pour le débogage. Évitez d'exposer des traces de pile, des noms de base de données ou des IDs internes. Un bon schéma :
message court et sûrextensions.code stable et lisible par machineretryable: true)Consignez l'erreur détaillée côté serveur avec un ID de requête pour enquêter sans exposer les détails internes.
Définissez un petit « contrat » d'erreur partagé entre web et mobile : valeurs extensions.code communes (ex. UNAUTHENTICATED, FORBIDDEN, BAD_USER_INPUT), quand afficher un toast vs une erreur inline, et comment traiter les données partielles. La cohérence évite à chaque client d'inventer ses propres règles d'erreur.
Les subscriptions sont la façon dont GraphQL pousse des données vers les clients lorsqu'elles changent, au lieu que le client interroge en permanence. Elles passent généralement par une connexion persistante (le plus souvent WebSockets), pour que le serveur envoie des événements dès qu'ils surviennent.
Une subscription ressemble beaucoup à une query, mais le résultat n'est pas unique : c'est un flux de résultats — chacun représentant un événement.
En pratique, un client "s'abonne" à un topic (par ex. messageAdded dans une application de chat). Quand le serveur publie un événement, les abonnés connectés reçoivent une payload correspondant à la sélection demandée par la subscription.
Les subscriptions excellent quand les utilisateurs attendent des changements instantanés :
Avec le polling, le client demande "Y a-t-il du nouveau ?" toutes les N secondes. C'est simple, mais cela peut gaspiller des requêtes (surtout quand rien ne change) et reste perçu comme moins réactif.
Avec les subscriptions, le serveur envoie l'update immédiatement. Cela réduit le trafic inutile et améliore la réactivité — au prix de connexions ouvertes et d'une infrastructure temps réel à gérer.
Les subscriptions ne sont pas toujours nécessaires. Si les mises à jour sont rares, non critiques en temps réel ou faciles à regrouper, le polling (ou le refetch après action utilisateur) suffit souvent.
Elles ajoutent aussi une charge opérationnelle : montée en charge des connexions, auth sur sessions longue durée, retries et monitoring. Règle pratique : utilisez les subscriptions seulement lorsque le temps réel est une exigence produit, pas simplement un plus agréable.
GraphQL est souvent décrit comme "donner du pouvoir au client", mais ce pouvoir a un coût. Connaître les compromis permet de décider quand GraphQL est adapté — et quand il serait excessif.
Le plus grand avantage est la flexibilité de la récupération des données : les clients peuvent demander exactement les champs nécessaires, ce qui réduit la sur-récupération et accélère les itérations UI.
Un autre atout majeur est le contrat fort fourni par le schéma GraphQL. Le schéma devient une source unique de vérité pour les types et opérations disponibles, ce qui améliore la collaboration et les outils.
Les équipes constatent souvent une meilleure productivité côté client : les développeurs frontend itèrent sans attendre de nouveaux endpoints, et des outils comme Apollo Client peuvent générer des types et simplifier la récupération des données.
GraphQL peut rendre la mise en cache plus complexe. Avec REST, le caching est souvent "par URL". Avec GraphQL, beaucoup de requêtes partagent le même endpoint, donc le caching repose sur la forme des requêtes, des caches normalisés et une configuration soignée côté serveur/client.
Côté serveur, il existe des pièges de performance. Une requête apparemment légère peut déclencher de nombreux appels en cascade à moins de concevoir les résolveurs avec soin (batching, éviter les N+1, contrôler les champs coûteux).
Il y a aussi une courbe d'apprentissage : schémas, résolveurs et patterns clients peuvent être nouveaux pour des équipes habituées aux APIs par endpoint.
Comme les clients peuvent demander beaucoup, les APIs GraphQL doivent appliquer des limites de profondeur et de complexité de requête pour prévenir les requêtes trop lourdes ou abusives.
L'authentification et l'autorisation doivent être appliquées par champ, pas seulement au niveau de la route, car différents champs peuvent avoir des règles d'accès différentes.
Opérationnellement, investissez dans le logging, le tracing et le monitoring adaptés à GraphQL : suivre les noms d'opération, les variables (avec prudence), les temps de résolveurs et les taux d'erreur pour repérer rapidement les requêtes lentes et les régressions.
GraphQL et REST permettent tous deux aux applications de communiquer avec des serveurs, mais ils structurent cette conversation très différemment.
REST est basé sur les ressources. Vous récupérez des données en appelant plusieurs endpoints (URLs) qui représentent des "choses" comme /users/123 ou /orders?userId=123. Chaque endpoint retourne une forme de données fixe décidée par le serveur.
REST s'appuie aussi sur les sémantiques HTTP : méthodes GET/POST/PUT/DELETE, codes de statut et règles de cache. Cela peut rendre REST naturel pour du CRUD simple ou pour tirer parti des caches navigateur/proxy.
GraphQL est basé sur un schéma. Au lieu de nombreux endpoints, vous avez généralement un endpoint, et le client envoie une requête décrivant les champs exacts qu'il veut. Le serveur valide cette requête contre le schéma GraphQL et renvoie une réponse correspondant à la forme demandée.
Cette sélection pilotée par le client est la raison pour laquelle GraphQL peut réduire la sur- et la sous-récupération, surtout pour des écrans UI qui demandent des données issues de plusieurs modèles liés.
REST est souvent préférable quand :
Beaucoup d'équipes mixent les deux :
La question pratique n'est pas "Lequel est meilleur ?" mais "Qu'est-ce qui convient à ce cas d'utilisation avec le moins de complexité ?".
Concevoir une API GraphQL est plus simple si vous la traitez comme un produit pour les auteurs d'écrans, pas comme un reflet de votre base de données. Commencez petit, validez avec des cas réels et étendez au fur et à mesure.
Listez vos écrans clés (ex. « Liste produits », « Détails produit », « Paiement »). Pour chaque écran, notez les champs exacts dont il a besoin et les interactions qu'il supporte.
Cela aide à éviter les "god queries", réduit la sur-récupération et clarifie où vous aurez besoin de filtrage, tri et pagination.
Définissez d'abord vos types cœur (ex. User, Product, Order) et leurs relations. Puis ajoutez :
Préférez des noms exprimant le langage métier plutôt que le nommage base de données. "placeOrder" communique mieux l'intention que "createOrderRecord".
Gardez un nommage cohérent : singulier pour un item (product), pluriel pour les collections (products). Pour la pagination, choisissez l'un :
Décidez tôt, car cela façonnera la structure de la réponse de votre API.
GraphQL supporte les descriptions directement dans le schéma — utilisez-les pour les champs, arguments et cas limites. Ajoutez ensuite quelques exemples copiable-collable dans vos docs (pagination et scénarios d'erreur courants). Un schéma bien décrit rend l'introspection et les explorateurs d'API bien plus utiles.
Commencer avec GraphQL consiste surtout à choisir quelques outils bien supportés et à mettre en place un workflow fiable. Vous n'avez pas à tout adopter d'un coup : faites fonctionner une requête de bout en bout, puis élargissez.
Choisissez un serveur selon votre stack et le niveau de fonctionnalités souhaité :
Une première étape pratique : définir un petit schéma (quelques types + une query), implémenter des résolveurs et connecter une source de données réelle (même une liste en mémoire simulée).
Si vous voulez passer plus vite de l'idée à une API fonctionnelle, une plateforme d'accélération comme Koder.ai peut vous aider à générer un petit app full-stack (React frontend, Go + PostgreSQL backend) et itérer sur schéma/resolvers via chat — puis exporter le code source quand vous voulez reprendre la main.
Côté frontend, le choix dépend souvent de votre préférence entre convention et flexibilité :
Si vous migrez depuis REST, commencez par utiliser GraphQL pour un écran ou une fonctionnalité, et conservez REST pour le reste jusqu'à ce que l'approche soit validée.
Traitez votre schéma comme un contrat d'API. Couches de tests utiles :
Pour approfondir :
GraphQL est un langage de requête et un runtime pour les API. Les clients envoient une requête décrivant précisément les champs souhaités, et le serveur renvoie une réponse JSON qui reflète cette structure.
On peut le voir comme une couche entre les clients et une ou plusieurs sources de données (bases de données, services REST, APIs tierces, microservices).
GraphQL aide principalement à résoudre :
En permettant au client de demander uniquement des champs spécifiques (y compris des champs imbriqués), GraphQL peut réduire le transfert de données superflu et simplifier le code client.
GraphQL n'est pas :
Traitez-le comme un contrat d'API + moteur d'exécution, pas comme une solution miracle de stockage ou de performance.
La plupart des APIs GraphQL exposent un endpoint unique (souvent /graphql). Plutôt que de multiplier les URLs, on envoie différentes opérations (queries/mutations) à ce même endpoint.
Implication pratique : la mise en cache et l'observabilité se basent souvent sur le nom de l'opération + les variables, et non sur l'URL.
Le schéma est le contrat de l'API. Il définit :
User, Post)User.name)User.posts)Parce qu'il est , le serveur peut valider les requêtes avant exécution et retourner des erreurs claires quand un champ n'existe pas.
Les queries GraphQL sont des opérations de lecture. Vous spécifiez les champs dont vous avez besoin, et la réponse JSON reflète la structure de la requête.
Conseils :
query GetUserWithPosts) pour faciliter le débogage et le suivi.posts(limit: 2)).Les mutations sont des opérations d'écriture (create/update/delete). Un schéma courant :
inputRetourner des données (plutôt que seulement success: true) aide l'UI à se mettre à jour immédiatement et garde les caches cohérents.
Les résolveurs sont des fonctions au niveau des champs qui indiquent comment obtenir ou calculer la valeur d'un champ.
Dans la pratique, un résolveur peut :
L'autorisation est souvent appliquée dans les résolveurs (ou via un middleware partagé), car ils savent qui demande quoi.
Il est facile de créer un motif N+1 (par ex. charger les posts séparément pour 100 utilisateurs).
Atténuations courantes :
Mesurez les temps d'exécution des résolveurs et surveillez les appels répétitifs vers des services en aval pendant une requête.
GraphQL peut retourner des données partielles avec un tableau errors. Cela se produit quand certains champs se résolvent et d'autres échouent (par ex. champ interdit, timeout d'un service en aval).
Bonnes pratiques :
message courts et sûrs pour l'utilisateurextensions.code (ex. FORBIDDEN, BAD_USER_INPUT)Les clients doivent décider quand afficher des données partielles ou considérer l'opération comme échouée.