Module 03 · Lesson 05

REST, GraphQL, gRPC: pick by shape of change

The right API style follows from who calls it and how often the contract changes.

Intermediate 8 min read Updated 2026-11-01

What you will be able to do

  • Match an API style to public, mobile, and service-to-service callers
  • Explain N+1 request and over-fetching problems and who solves them
  • Describe how gRPC's binary protocol and streaming change performance
  • Choose a pagination and versioning strategy

Three shapes

RESTGraphQLgRPC
ModelResources and verbsGraph queried by the clientRemote methods with typed messages
TransportHTTP/1.1 or 2, JSONUsually HTTP POST, JSONHTTP/2, protobuf
Who usually calls itPublic clients, browsers, partnersApps with varied screensInternal services
CachingFree via HTTP semanticsHard — single endpoint, custom keysLimited; HTTP caching mostly lost
VersioningURL or header versioningSchema evolution, deprecation fieldsField numbers, backward compatible
StreamingNot reallySubscriptions over websocketsBuilt in, both directions

The problem REST creates on a phone

A feed screen needs a post, an author, three comments, and a like count. REST gives you four or five endpoints, so the phone makes four or five round trips — the N+1 problem at the client. GraphQL lets the client ask for exactly those fields in one request, which on a mobile network is worth a lot.

query FeedScreen($id: ID!) {
  post(id: $id) {
    text
    author { name avatar }
    comments(first: 3) { text }
    likeCount
  }
}

The costs are real. Every field is a resolver, so a naive implementation becomes its own N+1 problem on the server — solved with batching (DataLoader-style). Caching is no longer free, so you need persisted queries, per-field cache hints, or a cache in front of the resolver layer. And an unbounded query can cost more than a thousand REST calls, so you must price queries and refuse expensive ones.

A defensible answer
"Public read APIs stay REST so partners and CDNs can cache them. The mobile app gets a GraphQL gateway over the same services to cut round trips. Internal calls are gRPC because we control both ends and want typed contracts and streaming." That is one sentence per audience and it shows you think in consumers, not fashion.

gRPC specifics worth naming

  • Protobuf payloads are compact and schema-checked at build time, which removes a class of integration bugs.
  • HTTP/2 multiplexes many calls over one connection, so a chatty service mesh does not pay a handshake per call.
  • Streaming modes — client, server, bidirectional — make it the natural fit for uploads, logs, and live updates.
  • It is awkward in browsers (needs a proxy) and hostile to eyeball debugging, so keep it inside the network.

Pagination, filtering, versioning

ApproachHowProblem
Offset/limit?offset=40&limit=20Rows shift when items are inserted; deep offsets scan everything
Cursor?after=<opaque cursor>, server returns nextSlightly more work to implement; no jumping to page 7
KeysetWHERE (created_at, id) < (?, ?)Needs a stable sort key; excellent for infinite scroll

Version by never breaking: add fields, deprecate loudly, keep old shapes alive for a measured period. If a breaking change is unavoidable, put the version in the path (/v2/) so it is visible in logs and easy to route. Header versioning is cleaner in theory and harder to debug in practice.

Instead
Returning the whole user object including email to every caller
Do this
Separate public and private representations; over-fetching is a privacy decision, not just a bandwidth one
Instead
One endpoint that takes an action verb (/doCheckout)
Do this
Model the noun and let the verb come from HTTP, unless the operation is genuinely a command
Instead
Versioning on day one for an internal API
Do this
Additive change and a deprecation window; version when a consumer cannot migrate

Worth remembering

  • REST: cacheable, obvious, best default for public and browser traffic.
  • GraphQL: one round trip for a screen that needs many resources; pays with caching complexity and query-cost limits.
  • gRPC: typed, compact, streaming — the default inside your own network.
  • Cursor pagination beats offsets once data changes underneath the reader.

Checkpoint

Answer all 3 questions to finish the lesson.
1. A mobile feed screen needs data from five related resources. Which style removes the client round trips most directly?
GraphQL lets one request describe the whole screen's data shape. REST would need several calls or a bespoke aggregate endpoint.
2. Why is cursor pagination preferred for a feed that receives new items constantly?
An offset counts positions in a changing list. A cursor anchors to a stable position, so new items do not push the reader's place around.
3. The strongest reason to prefer gRPC for internal service calls is:
gRPC's protobuf schema gives compile-time contract checking and compact messages, and HTTP/2 multiplexing avoids per-call connection overhead.