Ce que vous saurez faire
- Choisir REST ou GraphQL selon contexte
- Reconnaître forces/faiblesses
- Migrer progressivement
- Architecture hybride
Étape 1 — Cas d’usage décisif
Afficher fiche client + 5 dernières factures + lignes
# REST: 3-15 allers-retours
GET /clients/42
GET /clients/42/factures?limit=5
GET /factures/101/lignes
GET /factures/102/lignes
# ...
# GraphQL: UNE requête
query {
client(id: 42) {
nom
ville
factures(limite: 5) {
id numero montant
lignes { designation quantite pu }
}
}
}
Étape 2 — Over-fetching / Under-fetching
Over-fetching (REST):
GET /users/1 renvoie 30 champs alors que UI en utilise 3
Under-fetching (REST):
GET /users/1 puis GET /posts?user=1
GraphQL: { user(id:1) { name avatar posts { title } } }
Étape 3 — Cache HTTP
GET /products/42 HTTP/1.1
HTTP/1.1 200 OK
Cache-Control: public, max-age=300
ETag: "a1b2c3"
# CDN cache gratuit
GraphQL: POST unique sur /graphql. Cache HTTP absent. Cache applicatif requis (Apollo, urql, Relay).
Étape 4 — Typage
query Introspection {
__schema { types { name fields { name type { name } } } }
}
GraphQL: schéma introspectable natif. REST: OpenAPI/Swagger.
Étape 5 — Gestion erreurs
REST:
GET /clients/9999
HTTP/1.1 404 Not Found
{ "error": "not_found" }
GraphQL: toujours 200 OK
{
"data": { "client": null },
"errors": [{
"message": "Client introuvable",
"extensions": { "code": "NOT_FOUND" }
}]
}
Étape 6 — Upload fichier
REST: multipart/form-data natif
curl -X POST -F "fichier=@facture.pdf" https://api.example.sn/v1/uploads
GraphQL: spec graphql-multipart-request + libs. REST plus simple.
Étape 7 — Problème N+1 GraphQL
// Solution: DataLoader
import DataLoader from "dataloader";
const clientLoader = new DataLoader(async (ids) => {
const rows = await db.query(
"SELECT * FROM clients WHERE id = ANY($1)", [ids]);
return ids.map(id => rows.find(r => r.id === id));
});
const resolvers = {
Facture: {
client: (parent) => clientLoader.load(parent.clientId),
},
};
// 100 factures même client = 1 requête SQL
Étape 8 — Sécurité GraphQL
import depthLimit from "graphql-depth-limit";
const server = new ApolloServer({
typeDefs, resolvers,
validationRules: [depthLimit(5)], // Rejette > 5 niveaux
});
Sans limit: attaque user { posts { comments { author { posts ... } } } } peut tuer le serveur.
Étape 9 — Quand REST
- API publique tiers hétérogènes
- Uploads, webhooks, payloads binaires
- Besoin cache HTTP/CDN
- Contrats stables long terme (bancaire)
- Équipe peu expérimentée
Étape 10 — Quand GraphQL
- Front unique multi-plateforme (mobile + web + admin)
- Graphe de données complexe
- Mobile réseau faible (économie requêtes)
- Équipe back/front TypeScript + codegen
Étape 11 — Architecture hybride
/v1/uploads REST (multipart fichiers)
/v1/webhooks/stripe REST (signature vérifiée)
/v1/oauth/callback REST (standard OAuth)
/graphql GraphQL (CRUD métier)
/v1/admin/stats REST (cache CDN agressif)
Étape 12 — Migration progressive
- Garder REST existant
- Ajouter /graphql qui appelle mêmes services
- Migrer front principal → GraphQL
- Garder REST pour partenaires + clients legacy
- Déprécier progressivement endpoints REST sans clients
- Évaluer trimestriel: valeur vs maintenance dupliquée