Développement Web

API REST versus GraphQL : choisir la bonne approche

10 min de lecture

Ce que vous saurez faire

  1. Choisir REST ou GraphQL selon contexte
  2. Reconnaître forces/faiblesses
  3. Migrer progressivement
  4. 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

  1. Garder REST existant
  2. Ajouter /graphql qui appelle mêmes services
  3. Migrer front principal → GraphQL
  4. Garder REST pour partenaires + clients legacy
  5. Déprécier progressivement endpoints REST sans clients
  6. Évaluer trimestriel: valeur vs maintenance dupliquée

Etape 1 : poser le decor REST et GraphQL en 2026

REST a 26 ans, GraphQL en a 11. REST organise l’API autour de ressources accessibles via verbes HTTP (GET /users/42, POST /orders). GraphQL expose un seul endpoint POST /graphql qui execute des queries declaratives selectionnant exactement les champs requis. Les deux sont matures, ni l’un ni l’autre n’est universellement superieur. Le choix depend du domaine, de l’equipe et du client.

Pour une fintech a Dakar avec une app mobile sur reseau 3G intermittent, GraphQL reduit la bande passante en evitant l’over-fetching. Pour un microservice OpenBanking BCEAO qui doit respecter une norme stricte, REST avec OpenAPI 3.1 reste le standard sectoriel.

Etape 2 : comparer la maturite de l’ecosysteme

REST domine en outillage : OpenAPI 3.1, Swagger UI, Postman, generation de clients dans 40 langages avec openapi-generator, mocking instantane avec Prism. GraphQL gagne en outillage moderne : Apollo Studio, GraphiQL, code generation TypeScript via graphql-codegen, et le standard GraphQL over HTTP finalise en 2024.

Cote frameworks, REST tourne avec Express 5, FastAPI, Spring Boot 3, Laravel 12. GraphQL avec Apollo Server 4, GraphQL Yoga 5, Hot Chocolate (.NET) ou Strawberry (Python). Les deux ont des serveurs prets pour la production avec metriques Prometheus integrees.

Etape 3 : optimiser la bande passante mobile en zone 3G

A Bamako ou Cotonou, un utilisateur sur reseau Edge ou 3G charge une page profil. REST classique demande GET /users/42, GET /users/42/orders, GET /users/42/preferences soit 3 round trips et 18 Ko. GraphQL fait une seule query selectionnant uniquement les 5 champs affiches : 1 round trip, 2,4 Ko. Pour 100 000 sessions / mois, l’economie depasse 1,5 Go de bande passante.

query Profile { user(id: 42) { name avatar orders(last:5) { id total } prefs { lang } } }

Le signal de reussite : Chrome DevTools Network onglet montre une seule requete /graphql avec response size autour de 2 Ko et duree inferieure a 300 ms en 3G. Sur REST equivalent, on serait a 800 ms cumules.

Etape 4 : exposer une API publique BCEAO ou OpenBanking

Pour une banque qui ouvre une API a des fintechs partenaires, la conformite OpenBanking et la lisibilite par des outils standards prime. REST + OpenAPI 3.1 est le format attendu : Swagger UI auto-genere, Stoplight pour la doc, Kong ou Apigee pour la passerelle. GraphQL serait plus difficile a regulier (rate limit par champ, complexite de query) et moins reconnu par les auditeurs.

A l’inverse, pour une API interne entre microservices d’une meme equipe, GraphQL Federation v2 avec Apollo Router permet d’agreger des sous-graphes sans BFF (Backend For Frontend) supplementaire. Le choix doit suivre l’audience : externe regulee = REST, interne agile = GraphQL ou gRPC.

Etape 5 : comprendre le piege de la complexite des queries

GraphQL laisse le client choisir la profondeur des donnees. Un attaquant peut envoyer une query imbriquee 50 niveaux qui bloque la base de donnees. Mitigation : impose une depth limit de 7, un cost analysis avec graphql-cost-analysis, et un query timeout de 5 secondes. Sans ces gardes-fous, ton serveur GraphQL est une bombe DoS gratuite.

const server = new ApolloServer({ schema, plugins: [ depthLimit(7), costLimit({ maxCost: 1000 }) ] });

Le signal de reussite : une query malveillante de depth 10 reçoit une reponse 400 Bad Request en moins de 50 ms sans toucher la base. REST n’a pas ce probleme par construction car chaque endpoint est explicite.

Etape 6 : gerer la mise en cache HTTP

REST excelle au cache : Cache-Control, ETag, If-None-Match font partie du standard. Cloudflare ou Varnish cachent une reponse REST publique sans config particuliere et reduisent ta facture VPS de 70 %. Pour une API REST publique GET /produits a Dakar derriere Cloudflare, le hit ratio depasse souvent 85 %.

GraphQL avec POST /graphql contourne le cache HTTP standard. Apollo Client implemente un cache cote client (InMemoryCache) tres puissant, mais le CDN edge ne peut pas cacher car chaque query est differente. Solutions : automatic persisted queries (APQ) en GET, ou un cache cle-valeur cote serveur avec Redis et TTL court.

Etape 7 : choisir une strategie hybride GraphQL en facade et REST en interne

Beaucoup d’entreprises matures combinent : REST pour les APIs internes microservices (KISS, OpenAPI documenter), GraphQL en facade publique vers le mobile et le frontend Next.js. Apollo Server 4 lit plusieurs APIs REST internes via REST Data Source et expose un schema GraphQL unifie. Tu obtiens le meilleur des deux mondes sans tout reecrire.

Pour une fintech a Abidjan avec 8 microservices REST existants, cette migration progressive prend environ 6 semaines : 1 semaine pour le schema GraphQL initial, 4 semaines pour migrer les ecrans mobile critiques, 1 semaine pour decommissioner les anciens BFFs en duplicate. ROI mesure : -45 % bande passante mobile, -30 % temps de developpement frontend.

Etape 8 : grille de decision pour ton prochain projet

Choisis REST si : API publique regulee BCEAO ou ARTP, equipe junior moins de 3 ans d’experience, besoin de cache CDN agressif, ressources naturellement orientees CRUD. Choisis GraphQL si : multiples clients mobiles et web avec besoins divergents, equipe senior ayant deja affronte les pieges N+1 et complexity attack, plusieurs sources de donnees a federer, contraintes fortes de bande passante.

Pour étoffer le tableau : comparatif Gitea Forgejo GitHub et observabilite stack LGTM.

Etape 9 : monter un serveur REST minimal avec FastAPI

FastAPI 0.115 est aujourd’hui le framework REST Python le plus populaire grace a sa generation automatique d’OpenAPI 3.1, sa validation Pydantic v2 native et ses performances proches de Node.js. Cree un endpoint GET /products avec pagination en moins de 30 lignes.

from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Catalog API", version="1.0")
class Product(BaseModel):
    id: int; name: str; price_xof: int
@app.get("/products", response_model=list[Product])
def list_products(limit: int = 20, offset: int = 0):
    return db.fetch(limit, offset)

Le signal de reussite : http://localhost:8000/docs affiche Swagger UI auto-genere avec endpoints testables, et /openapi.json permet de generer un client TypeScript ou Dart pour ton app mobile en une commande openapi-generator.

Etape 10 : monter un serveur GraphQL minimal avec Apollo Server 4

Apollo Server 4 sur Node.js 22 LTS fait tourner un schema GraphQL avec subscriptions WebSocket en moins de 50 lignes. Le schema-first approach est plus lisible pour une equipe qui debute en GraphQL.

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = `type Product { id: ID! name: String! priceXof: Int! }
type Query { products(limit: Int = 20): [Product!]! }`;
const resolvers = { Query: { products: (_, { limit }) => db.list(limit) } };
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });

Le signal de reussite : http://localhost:4000 ouvre Apollo Sandbox avec autocomplete sur le schema. Tu testes la query products(limit: 5) et obtiens un JSON propre. La generation de types TypeScript pour le client se fait avec graphql-codegen en 4 lignes de config.

Etape 11 : eviter le probleme N+1 avec DataLoader

Le piege classique GraphQL : la query products { reviews { author { name } } } sur 50 produits genere 1 + 50 + 250 requetes SQL. DataLoader 2.2 batch les requetes en 1 seule SELECT par niveau, ramenant le total a 3. Sans DataLoader, ton serveur GraphQL s’effondre des 100 utilisateurs concurrents.

const reviewLoader = new DataLoader(productIds =>
  db.query("SELECT * FROM reviews WHERE product_id = ANY($1)", [productIds])
    .then(rows => productIds.map(id => rows.filter(r => r.product_id === id))));

Ajoute des metriques Prometheus sur le ratio de batch : un loader avec moins de 5 cles par batch en moyenne est suspect. Le signal de reussite : la latence p95 de la query reste sous 200 ms meme avec 100 produits demandes simultanement.

Etape 12 : authentification et autorisation par champ

REST traite l’autorisation au niveau endpoint via middleware (FastAPI Depends, Express middleware). GraphQL doit traiter l’autorisation par champ : un utilisateur authentifie peut voir user.email mais pas user.salaryXof. La librairie graphql-shield ou les directives @auth(role: « admin ») rendent ca declaratif.

Pour une fintech a Dakar avec roles client, agent, manager, definis 3 roles dans Authentik et applique-les via une directive @auth sur les champs sensibles. Un client qui interroge user { salaryXof } recoit un null avec error code 403, sans casser le reste de la query. Cette granularite est impossible en REST sans multiplier les endpoints.

Etape 13 : observer une API REST ou GraphQL en production

Instrumente avec OpenTelemetry pour obtenir traces, metriques et logs unifies. REST : compte requests par endpoint, status code et latence p95 dans Prometheus. GraphQL : compte par operation_name (donc par query nommee), avec extension Apollo metrics qui expose graphql_request_duration_seconds. Couple a Grafana Tempo pour traces distribuees, tu detectes une N+1 cachee en moins de 5 minutes.

Etape 14 : checklist mise en production d’une API en 2026

Douze points avant d’exposer ton API au public depuis Dakar ou Abidjan : OpenAPI 3.1 ou GraphQL schema versionne en Git, rate limiting par IP et par token (FastAPI slowapi ou Apollo plugin), authentification JWT ou OAuth2 avec refresh, autorisation par role ou par champ, validation stricte des inputs (Pydantic ou GraphQL scalars custom), CORS configure pour les domaines autorises, logs structures JSON avec request_id, metriques Prometheus exposees, traces OpenTelemetry, healthcheck /health pour les load balancers, documentation publique a jour, et tests d’integration couvrant les chemins critiques avec moins de 1 % de regressions par release. Sans ces 12 points, le passage en production est premature.

Etape 15 : roadmap d’evolution sur 12 mois pour une PME

Trimestre 1 : choisis REST ou GraphQL selon la grille etape 8, ecris la premiere version avec 5 endpoints critiques, deploie sur Coolify ou un VPS Hetzner avec TLS Let’s Encrypt. Trimestre 2 : ajoute observabilite OpenTelemetry, alerting Slack, autorisation fine. Trimestre 3 : automatise les tests E2E avec Playwright ou pytest et lance une beta a 10 partenaires. Trimestre 4 : audite la securite avec OWASP ZAP, optimise la bande passante mobile, et documente la procedure de deprecation pour les futures v2 v3. Cette trajectoire mene une API a un niveau enterprise en moins d’un an avec un budget VPS sous 60 000 FCFA annuels.

Partager