Jour 3 Day 3 · mercredi 29 juillet 2026 Wednesday 29 July 2026 Architecture Fondamental

REST vs GraphQL vs gRPC REST vs GraphQL vs gRPC

Trois façons de concevoir une API, trois familles de trade-offs. Savoir choisir — et surtout justifier — entre REST, GraphQL et gRPC est une question quasi systématique en entretien backend. Three ways to design an API, three families of trade-offs. Knowing how to choose — and above all justify — between REST, GraphQL and gRPC is an almost guaranteed question in backend interviews.

L’essentiel

Trois styles dominent la conception d’API. REST expose des ressources manipulées par les verbes HTTP — le standard de fait du web, universel et cacheable. GraphQL est un langage de requête typé : le client décrit exactement les données qu’il veut, le serveur les résout via un endpoint unique. gRPC est un framework RPC : on appelle des méthodes distantes définies dans un contrat protobuf, sérialisées en binaire sur HTTP/2.

Aucun n’est « meilleur » : ce sont des trade-offs. La question d’entretien n’est jamais « lequel est le mieux » mais « lequel choisirais-tu ici, et pourquoi ».

RESTGraphQLgRPC
TransportHTTP, JSON texteHTTP, POST /graphql uniqueHTTP/2, protobuf binaire
ContratOpenAPI (optionnel)Schéma typé (obligatoire).proto (obligatoire, codegen)
CachingHTTP natif (CDN, navigateur)Difficile (tout en POST)À la charge de l’application
StreamingNon (polling, SSE)SubscriptionsNatif, 4 modes
Cas d’usageAPI publiqueBFF, mobile, agrégationMicroservices internes

💡 La ligne décisive — le caching : REST l’a gratuitement (GET + CDN + navigateur), GraphQL le perd (tout passe en POST sur un endpoint unique). Pour une API publique très lue, c’est souvent l’argument qui clôt le débat.

Comment ça marche

REST (REpresentational State Transfer) : des ressources nommées par des URLs (/users/42/orders), manipulées par les verbes HTTP — GET (lire, sans effet de bord), POST (créer), PUT (remplacer), PATCH (modifier partiellement), DELETE. Les codes de statut portent le résultat : 200 OK, 201 Created, 204 No Content, 400 requête invalide, 401 non authentifié, 403 non autorisé, 404 introuvable, 409 Conflict, 422 erreur de validation, 500 erreur serveur. Stateless : chaque requête porte tout son contexte (token d’auth compris), le serveur ne garde pas de session en mémoire — ce qui rend le scaling horizontal trivial. HATEOAS, en une phrase : la contrainte REST « pure » où chaque réponse contient les liens vers les actions possibles — rarement implémentée en pratique, mais bon à citer.

GraphQL : un schéma fortement typé (types, queries, mutations, subscriptions) exposé sur un endpoint unique, généralement POST /graphql. Le client envoie une requête qui décrit précisément les champs voulus, y compris à travers les relations : fini l’over-fetching et l’under-fetching — les deux illustrés dans l’exemple ci-dessous. Côté serveur, chaque champ est produit par un resolver ; naïvement, une liste de N articles avec leur auteur déclenche 1 + N requêtes SQL — le fameux problème N+1, résolu par le batching (pattern DataLoader).

Le même écran « profil + dernières commandes », dans les deux styles :

# REST : deux allers-retours, toutes les données de chaque ressource
GET /users/42          # 40 champs reçus… pour en afficher 3
GET /users/42/orders   # deuxième appel pour compléter l'écran
# GraphQL : un seul aller-retour, exactement les champs voulus
query {
  user(id: 42) {
    name
    avatarUrl
    orders(last: 5) {   # la relation traversée dans la même requête
      total
      status
    }
  }
}

gRPC : contract-first — on écrit un fichier .proto (messages + services), le compilateur protobuf génère clients et serveurs dans la plupart des langages. Sérialisation binaire compacte (champs numérotés, pas de noms de clés répétés comme en JSON), transport HTTP/2 : multiplexage des appels sur une seule connexion, compression d’en-têtes. Quatre modes d’appel : unary (requête/réponse), server streaming, client streaming, streaming bidirectionnel. Deadlines propagées entre services et codes de statut dédiés complètent le contrat.

Concepts clés à maîtriser

  • Quand choisir quoi : API publique → REST (universel, testable au curl, cacheable par CDN et navigateurs). BFF (Backend For Frontend) ou client mobile qui agrège plusieurs sources → GraphQL (le client compose ses données en un aller-retour). Microservices internes à fort trafic → gRPC (contrats stricts, performance, streaming). Nuance à connaître : gRPC dans un navigateur nécessite grpc-web et un proxy.
  • Versioning : REST → /v1/ dans l’URL ou un header, avec la règle de ne jamais casser les clients existants. GraphQL → pas de versions : le schéma évolue de façon additive et les champs obsolètes sont marqués @deprecated. Protobuf → chaque champ a un numéro ; on n’en réutilise ni n’en renumérote jamais, on ajoute — backward compatible par construction.
  • Pagination : offset (?page=3&limit=20) simple mais instable si la liste bouge entre deux pages, et lente en profondeur ; cursor-based (un curseur opaque pointant après le dernier élément vu) stable et performante — le défaut des APIs modernes. En GraphQL, formalisée par la spec Relay Connections (edges/nodes/pageInfo).
  • Idempotence : une opération est idempotente si la rejouer produit le même état final. GET, PUT, DELETE le sont ; POST non. Crucial dès qu’il y a des retries réseau : pour un paiement, une idempotency key (header unique envoyé par le client, dédupliqué côté serveur) évite le double débit — la question piège classique.
  • Codes HTTP fins : distinguer 401 (« qui es-tu ? ») de 403 (« je sais qui tu es, tu n’as pas le droit »), 400 de 422, et savoir pourquoi renvoyer 200 avec {"error": ...} dans le body est un anti-pattern.

En entretien

🎤 En entretien — à « lequel choisirais-tu ? », la bonne réponse commence par « ça dépend », suivie de critères concrets : qui consomme (public, mobile, interne), besoin de cache, besoin de streaming. REST par défaut, GraphQL ou gRPC quand un besoin précis le justifie — dans cet ordre.

« C’est quoi, REST, au juste ? » — Un style d’architecture (thèse de Roy Fielding, 2000) : ressources identifiées par des URLs, interface uniforme HTTP (verbes + codes de statut), stateless, réponses cacheables. Points bonus : préciser que la plupart des « APIs REST » réelles sont du JSON-over-HTTP sans HATEOAS — et que c’est un compromis parfaitement assumé.

« PUT vs PATCH vs POST ? » — POST crée une ressource (non idempotent : deux POST = deux ressources). PUT remplace intégralement la ressource à l’URL donnée (idempotent : le rejouer ne change rien de plus). PATCH applique une modification partielle (pas garanti idempotent). Conséquence pratique : rejouer un PUT sur timeout est sûr, rejouer un POST ne l’est pas sans idempotency key.

« Quel problème résout GraphQL, et lesquels crée-t-il ? » — Il résout l’over-fetching et l’under-fetching : le client compose exactement ses données en un aller-retour, précieux sur mobile. Il crée : le N+1 côté resolvers (→ DataLoader), la perte du cache HTTP (tout passe en POST sur un endpoint unique), la nécessité de se protéger des requêtes arbitrairement profondes (depth limit, complexity budget), et une complexité serveur nettement supérieure.

« Pourquoi gRPC est-il plus performant que REST/JSON ? » — Sérialisation protobuf binaire, plus compacte et plus rapide à parser que du JSON textuel ; HTTP/2 qui multiplexe les appels sur une connexion persistante ; code client/serveur généré depuis le contrat, donc pas de validation ad hoc. Et le streaming natif, là où REST impose polling ou SSE.

« Comment gères-tu les retries sur un endpoint de paiement ? » — L’appel est un POST, donc non idempotent par nature. Le client génère une idempotency key unique par opération ; le serveur stocke le résultat de la première exécution sous cette clé et renvoie la même réponse aux retries. Stripe est l’exemple canonique à citer.

Pièges & idées reçues

⚠️ L’anti-pattern à bannir — renvoyer 200 OK avec {"error": ...} dans le body : le monitoring ne voit rien, les retries automatiques ne se déclenchent pas, un cache peut stocker l’erreur. Le code de statut fait partie du contrat.

  • « REST = du JSON sur HTTP » — REST est un ensemble de contraintes. POST /getUserById est du RPC déguisé : des verbes dans les URLs sont un signal d’alarme en code review.
  • « GraphQL remplace REST » — non : pour une API publique simple ou fortement cacheable, GraphQL ajoute de la complexité sans bénéfice. C’est un outil de composition de données, pas une évolution universelle de REST.
  • « gRPC partout, même en externe » — attention : illisible au curl sans outillage (grpcurl), support navigateur indirect (grpc-web + proxy), et un contrat binaire se déboggue moins vite qu’un JSON.
  • Statelessness mal compris : le serveur a évidemment un état (la base de données) ; c’est l’état de session qui ne doit pas vivre en mémoire d’une instance — sinon le load balancing et l’autoscaling cassent.
  • Pagination offset en profondeur : OFFSET 100000 force la base à parcourir puis jeter 100 000 lignes, et les éléments se décalent si des insertions arrivent entre deux pages. Le cursor règle les deux problèmes.

Pour aller plus loin

The essentials

Three styles dominate API design. REST exposes resources manipulated through HTTP verbs — the de facto standard of the web, universal and cacheable. GraphQL is a typed query language: the client describes exactly the data it wants, the server resolves it through a single endpoint. gRPC is an RPC framework: you call remote methods defined in a protobuf contract, serialized in binary over HTTP/2.

None is “better”: they are trade-offs. The interview question is never “which one is best” but “which would you choose here, and why”.

RESTGraphQLgRPC
TransportHTTP, textual JSONHTTP, single POST /graphqlHTTP/2, binary protobuf
ContractOpenAPI (optional)Typed schema (mandatory).proto (mandatory, codegen)
CachingNative HTTP (CDN, browser)Hard (everything is POST)Application’s responsibility
StreamingNo (polling, SSE)SubscriptionsNative, 4 modes
Use casePublic APIBFF, mobile, aggregationInternal microservices

💡 The decisive row — caching: REST gets it for free (GET + CDN + browser), GraphQL loses it (everything goes through POST on a single endpoint). For a heavily read public API, that’s often the argument that ends the debate.

How it works

REST (REpresentational State Transfer): resources named by URLs (/users/42/orders), manipulated through HTTP verbs — GET (read, no side effects), POST (create), PUT (replace), PATCH (partial update), DELETE. Status codes carry the outcome: 200 OK, 201 Created, 204 No Content, 400 bad request, 401 unauthenticated, 403 unauthorized, 404 not found, 409 Conflict, 422 validation error, 500 server error. Stateless: each request carries its full context (auth token included), the server keeps no session in memory — which makes horizontal scaling trivial. HATEOAS, in one sentence: the “pure” REST constraint where each response contains links to the possible next actions — rarely implemented in practice, but worth mentioning.

GraphQL: a strongly typed schema (types, queries, mutations, subscriptions) exposed on a single endpoint, usually POST /graphql. The client sends a query describing precisely the fields it wants, across relations too: no more over-fetching and no more under-fetching — both illustrated in the example below. Server-side, each field is produced by a resolver; naively, a list of N articles with their author triggers 1 + N SQL queries — the famous N+1 problem, solved by batching (the DataLoader pattern).

The same “profile + latest orders” screen, in both styles:

# REST: two round trips, every field of each resource
GET /users/42          # 40 fields received… to display 3
GET /users/42/orders   # second call to fill the screen
# GraphQL: one round trip, exactly the fields you want
query {
  user(id: 42) {
    name
    avatarUrl
    orders(last: 5) {   # the relation traversed in the same query
      total
      status
    }
  }
}

gRPC: contract-first — you write a .proto file (messages + services), the protobuf compiler generates clients and servers in most languages. Compact binary serialization (numbered fields, no repeated key names like JSON), HTTP/2 transport: call multiplexing over a single connection, header compression. Four call modes: unary (request/response), server streaming, client streaming, bidirectional streaming. Propagated deadlines between services and dedicated status codes complete the contract.

Key concepts to master

  • When to choose what: public API → REST (universal, curl-testable, cacheable by CDNs and browsers). BFF (Backend For Frontend) or a mobile client aggregating several sources → GraphQL (the client composes its data in one round trip). High-traffic internal microservices → gRPC (strict contracts, performance, streaming). Nuance to know: gRPC in a browser requires grpc-web and a proxy.
  • Versioning: REST → /v1/ in the URL or a header, with the rule of never breaking existing clients. GraphQL → no versions: the schema evolves additively and obsolete fields are marked @deprecated. Protobuf → each field has a number; you never reuse or renumber them, you only add — backward compatible by construction.
  • Pagination: offset (?page=3&limit=20) is simple but unstable if the list changes between pages, and slow at depth; cursor-based (an opaque cursor pointing after the last seen item) is stable and fast — the default of modern APIs. In GraphQL, formalized by the Relay Connections spec (edges/nodes/pageInfo).
  • Idempotence: an operation is idempotent if replaying it produces the same final state. GET, PUT, DELETE are; POST is not. Crucial as soon as network retries exist: for a payment, an idempotency key (a unique header sent by the client, deduplicated server-side) prevents double charging — the classic trick question.
  • Fine-grained HTTP codes: distinguish 401 (“who are you?”) from 403 (“I know who you are, you’re not allowed”), 400 from 422, and know why returning 200 with {"error": ...} in the body is an anti-pattern.

In an interview

🎤 In an interview — to “which would you choose?”, the right answer starts with “it depends”, followed by concrete criteria: who consumes (public, mobile, internal), caching needs, streaming needs. REST by default, GraphQL or gRPC when a specific need justifies it — in that order.

“What is REST, exactly?” — An architectural style (Roy Fielding’s dissertation, 2000): resources identified by URLs, the uniform HTTP interface (verbs + status codes), stateless, cacheable responses. Bonus points: note that most real-world “REST APIs” are JSON-over-HTTP without HATEOAS — and that this is a perfectly deliberate compromise.

“PUT vs PATCH vs POST?” — POST creates a resource (not idempotent: two POSTs = two resources). PUT fully replaces the resource at the given URL (idempotent: replaying it changes nothing more). PATCH applies a partial update (not guaranteed idempotent). Practical consequence: replaying a PUT on timeout is safe, replaying a POST is not without an idempotency key.

“What problem does GraphQL solve, and which does it create?” — It solves over-fetching and under-fetching: the client composes exactly its data in one round trip, precious on mobile. It creates: N+1 in resolvers (→ DataLoader), the loss of HTTP caching (everything goes through POST on a single endpoint), the need to guard against arbitrarily deep queries (depth limit, complexity budget), and significantly higher server complexity.

“Why is gRPC faster than REST/JSON?” — Binary protobuf serialization, more compact and faster to parse than textual JSON; HTTP/2 multiplexing calls over a persistent connection; client/server code generated from the contract, so no ad hoc validation. Plus native streaming, where REST forces polling or SSE.

“How do you handle retries on a payment endpoint?” — The call is a POST, so not idempotent by nature. The client generates a unique idempotency key per operation; the server stores the result of the first execution under that key and returns the same response to retries. Stripe is the canonical example to cite.

Pitfalls & misconceptions

⚠️ The anti-pattern to ban — returning 200 OK with {"error": ...} in the body: monitoring sees nothing, automatic retries never trigger, a cache may store the error. The status code is part of the contract.

  • “REST = JSON over HTTP” — REST is a set of constraints. POST /getUserById is RPC in disguise: verbs in URLs are a red flag in code review.
  • “GraphQL replaces REST” — no: for a simple or heavily cacheable public API, GraphQL adds complexity with no benefit. It’s a data composition tool, not a universal evolution of REST.
  • “gRPC everywhere, even externally” — careful: unreadable with curl without tooling (grpcurl), indirect browser support (grpc-web + proxy), and a binary contract is slower to debug than JSON.
  • Statelessness misunderstood: the server obviously has state (the database); it’s session state that must not live in an instance’s memory — otherwise load balancing and autoscaling break.
  • Offset pagination at depth: OFFSET 100000 forces the database to scan then discard 100,000 rows, and items shift if insertions happen between two pages. Cursors solve both problems.

Going further

S'entraîner sur ce sujet → Practice this topic →