SYSTEM DESIGN · CONCEPTS · SERVICES AND HOW THEY TALK · EPISODE 8

API Design: REST vs RPC vs GraphQL

How clients talk to a backend: REST resources and HTTP verbs, status codes, RPC and gRPC, GraphQL and the over-fetching problem, pagination with cursors, versioning, and how to sketch an API in a system design interview.

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
1 / 11

Press play to hear the narration, or step through at your own pace. Every picture here is the same one used in the YouTube video.

Notes

The contract

An API is a contract. The client never touches your database. It only knows which operations exist, what they take, what they return and which errors can happen. That separation is the point: you can change the database underneath, as long as the contract stays the same.

An API is a contract
  • the client never sees the DB
  • it only sees the API:
  • which operations exist
  • what inputs they take
  • what they return
  • which errors can happen
  • change the DB freely,
  • keep the contract stable

REST

REST is the default for public web APIs. URLs name resources, nouns like users and posts, and the HTTP method is the verb: GET reads, POST creates, PUT replaces, PATCH updates part, DELETE removes. Status codes carry meaning: 200 OK, two oh one created, four hundred bad input, 404 not found, 5xx means it's our bug.

REST
resources are nouns, verbs are HTTP

GET    /users/42
GET    /users/42/posts
POST   /posts
PUT    /posts/7
PATCH  /posts/7
DELETE /posts/7

200 OK · 201 Created
400 bad input · 404 not found
429 slow down · 500 our bug

RPC

RPC means calling a function on another machine as if it were local: charge payment, send message. gRPC is the popular version: it runs on HTTP two with protocol buffers, so messages are small, binary and strongly typed, and it supports streaming. It shines between your own services; from browsers it's awkward.

RPC and gRPC
call a function on another machine

service Payments {
  rpc Charge(ChargeReq)
      returns (ChargeResp);
}

gRPC: HTTP/2 + protobuf
binary, typed, fast, streaming
great between services
awkward from browsers

Over-fetching

REST has a classic pain. A profile screen needs a name, an avatar and the last three posts with comment counts. With fixed endpoints the app makes several calls, each returning far more fields than it shows. On a slow phone network, those round trips and wasted bytes are what make the screen slow.

The profile screen
  • needs: name, avatar,
  • last 3 posts, comment counts
  • REST gives:
  • /users/42 → 40 fields
  • /users/42/posts → 50 posts
  • /posts/:id/... → per post
  • too many round trips
  • and too much data

GraphQL

GraphQL flips it. There's one endpoint, and the client sends a query listing exactly the fields it wants, nested: this user's name, avatar, last three posts and their comment counts. One round trip, no wasted fields. The costs: HTTP caching is harder, and a single deep query can be very expensive, so servers limit query depth and cost.

GraphQL
query {
  user(id: 42) {
    name avatar
    posts(last: 3) {
      title commentCount
    }
  }
}

one request, exactly these fields
cost: caching is harder,
and one query can be very expensive

Pagination

Never return an unbounded list. Offset pagination, page five hundred, makes the database skip thousands of rows, and items shift when new ones arrive. Cursor pagination says 'give me twenty after this id', which is a fast indexed range query and stays stable while you scroll. Return the next cursor with each page.

Pagination
offset: ?page=500&size=20
  slow: DB skips 10,000 rows
  rows shift while you scroll

cursor: ?after=post_9831&size=20
  WHERE id < 9831
  ORDER BY id DESC LIMIT 20
  fast with an index, stable

return next_cursor in the reply

Versioning

Old app versions stay installed for years, so your API can't break them. Adding optional fields or new endpoints is safe. Removing or renaming fields is a breaking change and needs a new version, like slash v two. Also: idempotency keys on POSTs, rate limits per client, and authentication on every endpoint.

Versioning and safety
  • old app versions live for years
  • additive changes are safe:
  • new optional fields
  • new endpoints
  • breaking changes need a version:
  • /v2/... or a version header
  • POST with Idempotency-Key
  • rate limits per API key
  • auth on every endpoint

Decision

Which one? REST for public APIs and simple create, read, update, delete. gRPC for high-volume calls between your own services. GraphQL when many different clients need flexible, nested data for rich screens. Plenty of large companies use all three, each where it fits.

Which one?
  • public API, simple CRUD → REST
  • internal service calls → gRPC
  • many clients, rich screens,
  • changing needs → GraphQL
  • many companies use all three:
  • GraphQL for apps,
  • gRPC inside,
  • REST for partners

Say it like this

In an interview, write three to five endpoints for the core features, with inputs and outputs: post a tweet returns its id, get timeline takes a cursor and returns tweets plus the next cursor. Then say it out loud: REST with JSON, a token for auth, cursor pagination, and idempotency keys on writes.

In the interview
POST /tweets {text}
  → 201 {id}
GET  /timeline?cursor=…
  → {tweets[], next_cursor}
POST /users/:id/follow

say: REST, JSON, auth token,
cursor pagination,
idempotency key on POST

Interview questions

What is the difference between REST and RPC?

REST models the API as resources (nouns) manipulated with standard HTTP methods and status codes. RPC models it as remote procedure calls (verbs), such as chargePayment. gRPC is a typed, binary RPC framework over HTTP/2.

When would you choose GraphQL over REST?

When many clients need different, nested slices of data for rich screens, so fixed REST endpoints cause over-fetching or many round trips. The trade-offs are harder HTTP caching and the need to limit query depth and cost.

Why is gRPC popular between microservices?

It uses compact binary protocol buffers over HTTP/2, gives strongly typed generated clients, supports streaming and is fast. Browser support is weaker, so it is mostly used service to service.

Offset or cursor pagination?

Cursor pagination. Large offsets force the database to scan and skip rows and results shift as data changes. A cursor (for example the last id seen) becomes an indexed range query that is fast and stable.

How do you version an API without breaking clients?

Make additive, backward-compatible changes by default (new optional fields, new endpoints). For breaking changes, introduce a new version via the path or a header and keep the old one until clients migrate.

Which HTTP status codes should an API use?

200 for success, 201 for created, 204 for no content, 400 for invalid input, 401 unauthenticated, 403 forbidden, 404 not found, 409 conflict, 429 rate limited, and 5xx for server errors.

What should you say about APIs in a design interview?

List the core endpoints with request and response shapes, the auth method, pagination style, idempotency for writes, and rate limiting. Keep it to the features in scope.

What is over-fetching and under-fetching?

Over-fetching is receiving more fields than the client needs; under-fetching is needing several requests to build one screen. Both waste bandwidth and latency; GraphQL or purpose-built endpoints address them.