REST, GraphQL and gRPC
Three ways to shape an API — resources over HTTP, a typed query language, and generated RPC over HTTP/2 — and the trade-offs that decide between them.
After this lesson you can
- Explain over-fetching and under-fetching and how GraphQL addresses them
- Name what GraphQL costs in caching, error handling and query control
- Say where gRPC fits and why browsers cannot call it directly
- Recommend a style for a given set of clients and constraints
REST, GraphQL and gRPC are not competing versions of one thing. They make different trade-offs, and an interviewer asking "which would you use?" wants to hear the trade-offs, not a favourite.
REST
Resources identified by URIs, manipulated with HTTP methods, with status
codes, caching, conditional requests and content negotiation all coming
from HTTP itself — the subject of this whole course. It works with every
client, every proxy and every CDN, and is easy to explore with nothing
but curl.
Its weak spot is fixed response shapes. A screen that needs a user's name and the titles of their last three orders either receives far more than it needs (over-fetching) or makes several requests to assemble it (under-fetching), each one a round trip on a mobile network.
GraphQL
A typed schema plus a query language: the client says exactly which fields it wants, nested as deep as the schema allows, and gets that shape back in one response.
query {
user(id: "7") {
name
orders(last: 3) { title }
}
}
const users = { 7: { id: "7", name: "Nino", email: "nino@example.com", address: "Tbilisi", plan: "pro" } };const orders = { 7: [ { id: "o1", title: "Desk lamp", total: 4200, items: 1, status: "delivered" }, { id: "o2", title: "Notebook", total: 900, items: 3, status: "shipped" }, ],}; // REST: fixed shapes, one request per resource.function restScreen(id) { const calls = []; const get = (path, body) => { calls.push(path); return body; }; const user = get(`/users/${id}`, users[id]); const list = get(`/users/${id}/orders`, orders[id]); const bytes = JSON.stringify(user).length + JSON.stringify(list).length; return { requests: calls.length, bytes, name: user.name, titles: list.map((o) => o.title) };} // GraphQL-style: the client names the fields, one response.function graphScreen(id) { const body = { user: { name: users[id].name, orders: orders[id].map((o) => ({ title: o.title })) } }; return { requests: 1, bytes: JSON.stringify(body).length, name: body.user.name, titles: body.user.orders.map((o) => o.title) };} export function run() { return { rest: restScreen(7), graphql: graphScreen(7) };}What it costs:
- HTTP caching mostly goes away. Queries are usually POSTed to a single endpoint, so CDNs and browser caches see one URL; caching moves into client libraries and the server.
- Errors live in the body. A GraphQL response has
dataanderrors, and can be a partial success — some fields resolved, others null with an error. Many servers answer such responses with HTTP 200, so monitoring must read the body, not the status code. - Clients can write expensive queries. A deeply nested query can fan out into thousands of database calls; servers need depth or complexity limits, and batching in resolvers to avoid the N+1 query problem.
- It evolves by adding fields and marking old ones
@deprecated, rather than by versioning — which works because clients only receive the fields they asked for.
GraphQL shines when many different clients (web, iOS, Android, partners) need different slices of a rich, connected domain.
gRPC
An RPC framework: services and messages are defined in Protocol Buffers
(.proto files), client and server code is generated for many languages,
and calls travel as compact binary messages over HTTP/2.
service Orders {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc WatchOrders (WatchRequest) returns (stream OrderEvent);
}
It supports unary calls and server, client and bidirectional
streaming, has deadlines built in, and uses its own status codes
(NOT_FOUND, UNAVAILABLE, DEADLINE_EXCEEDED, …) instead of HTTP
ones. Compatibility rules are strict and simple: add fields with new
numbers, never reuse or renumber an existing field.
Exercise · Pro
Resolve a field selection
Exercises are free in the first 3 lessons of every course. From here on they are part of Pro and Sprint; the lesson and its runnable examples stay open.
Upgrade