GraphQL est un langage de requêtes pour les API et un runtime côté serveur qui renvoie exactement les champs demandés par chaque client en une seule requête. La dualité compte : le langage est une spécification que n’importe quel client peut parler ; le runtime exécute ces requêtes contre un système de types que vous définissez au-dessus de vos données existantes — n’importe quelle base de données, n’importe quel service. Ce n’est pas une base de données, et il ne remplace ni votre stockage ni, nécessairement, votre API REST.
Points clés
| Question | Réponse |
|---|---|
| Ce que c’est | Un langage de requêtes régi par une spec + un runtime d’exécution — agnostique au stockage |
| Le geste signature | La forme de la réponse reflète celle de la requête : demandez des champs, recevez ces champs |
| Les trois opérations | query (lecture) · mutation (écriture) · subscription (push temps réel) |
| Les briques | Schéma (contrat SDL) · types · resolvers (fonctions de récupération par champ) |
| La facture honnête | Stratégie de cache, batching du N+1, limites par coût, durcissement de la sécurité |
La démo signature : requête et réponse
La démo vers laquelle converge chaque explication, parce qu’elle est l’idée — la réponse est la requête, remplie :
# Requête # Réponse
{ {
post(id: "8fk2") { "data": {
title "post": {
author { "title": "Hello GraphQL",
username "author": {
} "username": "ada"
comments(first: 2) { },
text "comments": [
} { "text": "Nice." },
} { "text": "Ship it." }
} ]
}
}
}
Une requête, trois ressources liées, zéro champ non demandé — le duo overfetching et underfetching mis à la retraite d’un coup. L’appeler depuis de vrais clients, c’est du HTTP ordinaire :
// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
method: 'POST',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Client-Key': CLIENT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
});
const { data } = await res.json(); // shaped exactly like the query // Flutter / Dart — query Back4app's auto-generated GraphQL API
final res = await http.post(
Uri.parse('https://parseapi.back4app.com/graphql'),
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Client-Key': clientKey,
'Content-Type': 'application/json',
},
body: jsonEncode({
'query': '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
);
final data = jsonDecode(res.body)['data']; // shaped exactly like the query // iOS / Swift — query Back4app's auto-generated GraphQL API
var request = URLRequest(url: URL(string: "https://parseapi.back4app.com/graphql")!)
request.httpMethod = "POST"
request.setValue(appId, forHTTPHeaderField: "X-Parse-Application-Id")
request.setValue(clientKey, forHTTPHeaderField: "X-Parse-Client-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let query = "{ posts(first: 20) { edges { node { title author { username } } } } }"
request.httpBody = try JSONEncoder().encode(["query": query])
let (data, _) = try await URLSession.shared.data(for: request)
// data is shaped exactly like the query // Android / Kotlin — query Back4app's auto-generated GraphQL API
val body = """{ "query": "{ posts(first: 20) { edges { node { title author { username } } } } }" }"""
val request = Request.Builder()
.url("https://parseapi.back4app.com/graphql")
.addHeader("X-Parse-Application-Id", APP_ID)
.addHeader("X-Parse-Client-Key", CLIENT_KEY)
.post(body.toRequestBody("application/json".toMediaType()))
.build()
val data = client.newCall(request).execute().body?.string() // shaped like the query Schéma, query, resolver : le trio qui fait le travail
Les explications montrent la requête ; presque aucune ne montre la machinerie derrière elle comme un tableau cohérent. Le schéma est le contrat typé, écrit en SDL :
type Post {
title: String! # ! = non nul
author: User!
comments(first: Int): [Comment!]
}
type Query { # les points d'entrée en lecture
post(id: ID!): Post
}
type Mutation { # les points d'entrée en écriture
createPost(title: String!): Post!
}
Les resolvers sont l’autre moitié du runtime — une fonction par champ, chacune libre d’aller chercher ses données n’importe où :
const resolvers = {
Query: {
post: (_, { id }) => db.posts.findById(id),
},
Post: {
author: (post) => db.users.findById(post.authorId), // appelée par post !
},
};
L’exécution est un pipeline : parser la requête, la valider contre le schéma (les opérations invalides meurent avant de toucher aux données), puis parcourir le selection set en appelant les resolvers et assembler le JSON miroir. Cet appel de resolver par champ est aussi le prix de la flexibilité — notez le // appelée par post !, qui devient le problème N+1 plus bas. Le nom, que personne n’explique : vos données forment un graphe d’objets typés, et les requêtes le traversent depuis les champs racine — mais seulement le long des chemins exposés par le schéma, pas en traversées arbitraires comme dans un vrai langage de requêtes sur graphes.
Provenance, en bref : créé chez Facebook (aujourd’hui Meta) en 2012 pour ses apps mobiles, publié en open-source en 2015, gouverné depuis 2018 par la GraphQL Foundation sous la Linux Foundation, édition actuelle de la spec d’octobre 2021, avec un brouillon GraphQL over HTTP qui standardise les conventions de transport.
Queries, mutations, subscriptions
Les queries lisent. Les mutations écrivent — et sélectionnent des champs sur le résultat, donc le client reçoit l’état post-écriture dans le même aller-retour. Les subscriptions gardent une connexion ouverte (en pratique, WebSockets) et poussent les événements au fil de l’eau ; elles sont le volet temps réel de GraphQL, avec cette réserve que chaque subscription active est de l’état tenu par le serveur. Les trois partagent le schéma, le système de types et l’outillage — un contrat, trois temps.
GraphQL vs. REST
| GraphQL | REST | |
|---|---|---|
| Endpoints | Un seul (/graphql) | Un par ressource |
| Forme de la réponse | Composée par le client à chaque requête | Fixe par endpoint |
| Over/underfetching | Résolu au niveau HTTP | Atténué par des paramètres |
| Cache HTTP | Perdu par défaut (un seul POST) | Natif — le superpouvoir |
| Typage et introspection | Intégrés au contrat | Optionnels via OpenAPI |
| Versionnage | Évolution sans versions + @deprecated | Conventions /v1, /v2 |
| Temps réel | Subscriptions dans la spec | Hors périmètre |
| Point fort | Clients variés, données imbriquées | CRUD de ressources, lectures cacheables |
L’argument complet — y compris quand REST est tout simplement le meilleur choix — vit dans l’entrée dédiée GraphQL vs. REST.
GraphQL en production : les coûts honnêtes
La section que les explications commerciales adoucissent. Cache : un seul endpoint POST renonce au cache HTTP et CDN indexé par URL ; le remplacement, ce sont des caches normalisés côté client indexés sur id plus __typename, et des persisted queries (opérations pré-approuvées et hachées, envoyées en GET) pour regagner une partie du cache de transport. N+1 : des resolvers naïfs transforment une liste de 20 posts en 1 + 20 lectures en base — le même problème que les clients REST subissent sur HTTP, déplacé dans votre couche de resolvers et corrigé là par des loaders de batching comme DataLoader. Erreurs : GraphQL renvoie 200 OK avec un tableau errors — le monitoring basé sur les codes de statut devient aveugle si on ne le rééduque pas. Rate limiting : les requêtes ne se valent pas quand une seule peut imbriquer dix relations ; les API matures mesurent le coût de la requête (analyse de profondeur et de complexité), pas le nombre de requêtes. Sécurité : désactivez l’introspection en production, imposez des limites de profondeur et de complexité, et gardez l’autorisation dans la couche métier sous les resolvers — l’endpoint unique aveugle aussi les règles de WAF basées sur l’URL, donc la validation migre dans la couche GraphQL elle-même.
Cas d’usage courants
- Apps mobiles sur réseaux contraints — le cas d’usage fondateur : champs exacts, octets minimaux, moins d’allers-retours.
- Produits multi-clients — app montre, app téléphone, dashboard web, chacun façonnant ses propres réponses contre un seul schéma.
- Agrégation backend-for-frontend — une couche GraphQL composant plusieurs services internes pour la consommation par les UI.
- Frontends qui évoluent vite — de nouveaux écrans sélectionnent de nouveaux champs sans attendre de nouveaux endpoints.
- Contrats typés de bout en bout — l’introspection du schéma génère des clients typés, gardant l’API et l’UI honnêtes à la compilation.
Devriez-vous utiliser GraphQL ? Matrice de décision
| GraphQL mérite sa machinerie quand… | Préférez REST quand… |
|---|---|
| Les clients diffèrent dans les données dont ils ont besoin | Un seul type de client, des écrans stables |
| Les écrans lisent des données imbriquées et relationnelles | Les ressources mappent proprement sur des endpoints |
| Vous agrégez plusieurs sources backend | Un seul service possède les données |
| La bande passante est précieuse (mobile-first) | Le cache HTTP/CDN peut porter la charge de lecture |
| Une plateforme génère le schéma pour vous | L’équipe doit tout construire et durcir à la main |
Limites et trade-offs
- La flexibilité est payée par le serveur. Des requêtes client arbitraires exigent que le serveur soit sûr sous n’importe quelle forme — batching, limites de coût et garde-fous de profondeur sont des prérequis, pas de la finition.
- Le cache devient votre projet. Ce que HTTP donnait gratuitement à REST, les équipes GraphQL le réimplémentent dans des caches client et des persisted queries.
- L’observabilité est à réapprendre. Un seul endpoint, des réponses toujours en 200 et un timing par champ exigent un outillage qui comprend GraphQL.
- Les uploads de fichiers et les données binaires sont malcommodes — généralement délégués à des endpoints d’upload séparés, à côté du graphe.
- La gouvernance du schéma est organisationnelle. Un contrat partagé entre équipes a besoin de règles d’ownership ; la fédération (composer des subgraphs détenus par chaque équipe en un supergraph) est la réponse à l’échelle — et une discipline en soi.
GraphQL sur Back4app
Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l’authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. La partie distinctive, c’est d’où vient le schéma : définissez un modèle de données et la plateforme génère l’API GraphQL — types d’objet typés, champs de query et de mutation, connections qui traversent les relations comme l’exemple posts → author ci-dessus — sans resolvers à écrire, puisque Back4app les implémente contre votre base de données avec des permissions appliquées à chaque requête. Les onglets de code montrent toute l’histoire côté client : un POST vers /graphql avec les clés de votre app. Une console GraphQL intégrée couvre l’exploration, REST reste disponible sur les mêmes données pour des lectures compatibles avec le cache, et la logique sur mesure rejoint le schéma sous forme de fonctions Cloud Code — la section des coûts honnêtes ci-dessus devient, pour l’essentiel, la facture de la plateforme, pas la vôtre.
Questions fréquentes
Qu'est-ce que GraphQL en termes simples ?
Un langage de requêtes qui permet à un client de demander à une API exactement les champs dont il a besoin — relations imbriquées comprises — en une seule requête, plus un runtime serveur qui sert ces requêtes depuis vos sources de données existantes. Il se place devant n'importe quelle base de données ou service ; ce n'est pas une base de données en soi.
GraphQL est-il meilleur que REST ?
Aucun des deux n'est universellement meilleur. GraphQL gagne avec des clients variés, des contraintes de bande passante et des données agrégées depuis plusieurs sources ; REST gagne sur le cache HTTP, la simplicité et la maturité de l'outillage pour du CRUD en forme de ressources. Le pattern dominant en production est pragmatique : une couche GraphQL pour les frontends au-dessus de services internes REST ou RPC.
GraphQL est-il une base de données, ou quelque chose comme SQL ?
Non — c'est un langage d'API de la couche applicative, agnostique au stockage par conception : les resolvers peuvent lire depuis n'importe quelle base de données, une autre API ou un fichier. Et malgré son nom, ce n'est pas un langage général de requêtes sur graphes comme SPARQL ; vous ne traversez le graphe que le long des chemins que le schéma expose.
Que sont les queries, mutations et subscriptions ?
Les trois types d'opération. Les queries lisent des données ; les mutations les écrivent — et renvoient le nouvel état dans le même aller-retour, donc le client se met à jour sans lecture supplémentaire ; les subscriptions poussent des mises à jour en temps réel sur une connexion persistante, typiquement WebSockets. Les trois sont validées contre le même schéma.
Qu'est-ce qu'un schéma GraphQL ?
Le contrat typé entre client et serveur, écrit dans le Schema Definition Language : les types d'objet, leurs champs et les points d'entrée racine Query, Mutation et Subscription. Chaque opération entrante est validée contre lui avant exécution, et l'outillage l'introspecte pour générer la documentation et des clients typés.
Qu'est-ce qu'un resolver ?
Une fonction côté serveur qui récupère la valeur d'un champ — depuis une base de données, une autre API ou n'importe où. Le runtime parcourt chaque requête et appelle le resolver de chaque champ demandé, ce qui fait à la fois la flexibilité de GraphQL et l'origine de son problème N+1 quand les resolvers des éléments d'une liste interrogent chacun séparément.
GraphQL ne fonctionne-t-il que sur HTTP POST ?
Par spécification, GraphQL est agnostique au transport ; en pratique, il est servi sur un seul endpoint HTTP — par convention /graphql — généralement via POST avec un corps JSON, GET étant permis pour les queries et WebSockets portant les subscriptions. Une spécification GraphQL over HTTP standardise désormais ces conventions.
Quand ne devriez-vous PAS utiliser GraphQL ?
Du CRUD simple sur des ressources avec des clients uniformes, du trafic de lecture que le cache HTTP et CDN pourrait absorber, des transferts lourds de fichiers, et de petites équipes sans appétit pour le batching des resolvers, la limitation par coût de requête et la gouvernance du schéma. Dans ces cas, une API REST bien conçue est moins de machinerie pour le même résultat.