Pick by constraints, not dogma
REST, gRPC, and GraphQL solve overlapping problems with different defaults. Interviewers want a decision framework: clients, caching, team workflow, network path, and evolution story — not “we use X because it is modern.”
Use this comparison when choosing an external API, an internal service boundary, or a BFF layer in front of both.
Side-by-side comparison
| Dimension | REST (HTTP resources) | RPC / gRPC | GraphQL |
|---|---|---|---|
| Mental model | Resources + HTTP verbs + URLs | Procedures + IDL + generated stubs | Schema + queries on one endpoint |
| Typical transport | HTTP/1.1 or HTTP/2; cache-friendly GET | HTTP/2; binary protobuf | Often POST /graphql |
| Contract | OpenAPI / Swagger common | .proto — strong codegen |
GraphQL schema + codegen |
| Caching | HTTP/CDN caching of reads works naturally | Mostly application-level | Needs design (persisted queries, GET, client stores) |
| Streaming | SSE, WebSockets as separate choices | First-class unary + streaming | Subscriptions (often WebSocket) |
| Browser / public internet | Easiest path | gRPC-Web or BFF often required | Works; watch POST caching |
| Over/under-fetching | Fixed response shapes per endpoint | Fixed per RPC message | Client selects fields |
| Good fit | Public APIs, CRUD, broad tooling | Service-to-service, high perf, streams | Flexible UIs, nested graphs, mobile |
JSON-RPC and ad hoc “RPC over REST paths” (POST /doThing) sit between styles — procedure-shaped without gRPC’s full stack.
REST — default for many external APIs
Choose REST when:
- Clients are diverse (third parties, scripts, browsers) and expect curl-friendly JSON.
- Cacheable reads matter — CDN edge cache for GET resources.
- Domain maps cleanly to resources and CRUD-ish operations.
- Team knows OpenAPI, HTTP status codes, and standard middleware.
One line: resource-oriented URLs + HTTP semantics; strong for cacheable reads and CRUD domains.
Watch out: RPC-shaped POST everywhere; session state; APIs that need many bespoke aggregations per screen (GraphQL or BFF may help).
gRPC — default for many internal platforms
Choose gRPC when:
- Callers are your services with generated stubs in the same org.
- You need low latency, high throughput, binary payloads.
- Streaming (logs, replication, live data) is core to the interface.
- Strict proto contracts and backward-compatible field evolution are valued.
One line: procedure + arguments; great for actions, typed contracts, and streaming inside the platform.
Watch out: public browser clients; corporate networks blocking HTTP/2 gRPC; need for CDN caching of reads.
Pattern: gRPC internally, REST or GraphQL at the edge via BFF.
GraphQL — default for some product-facing aggregators
Choose GraphQL when:
- Many client teams need different field sets from the same backend graph.
- Nested reads dominate and REST causes chatty waterfalls or over-fetching.
- You can invest in resolver performance (N+1 prevention), complexity limits, and field auth.
One line: client-driven field trees; one endpoint; mind N+1, auth per field, and caching.
Watch out: simple CRUD public APIs where REST + OpenAPI is enough; teams without GraphQL operational experience; cache-heavy public read surfaces without persisted query strategy.
Decision flow (interview whiteboard)
Who are the clients?
├─ External / unknown third parties → lean REST (+ OpenAPI)
├─ Browser-only product UI → REST or GraphQL (shape churn?)
└─ Internal services only → lean gRPC
Is read caching at CDN critical?
├─ Yes, mostly GET reads → REST
└─ No → gRPC or GraphQL acceptable
Is streaming core?
├─ Yes → gRPC (or REST + SSE/WebSocket for simpler cases)
└─ No → any
Do clients need different field shapes per platform?
├─ Yes, heavily → GraphQL or BFF over REST/gRPC
└─ No → REST or gRPC
State assumptions aloud. “Public partners → REST v1. Mobile app hits BFF. BFF aggregates gRPC from five services.” That sentence wins interviews.
Combining styles
Real systems mix:
- REST for public CRUD and webhooks
- gRPC between core services
- GraphQL on a BFF for the mobile app
- Async events (Kafka) for decoupling — not a replacement for sync API choice but part of the same design conversation
Consistency of error model, auth, and tracing across layers matters more than forcing one style everywhere.
Interview framing
“Why REST for this design?” — Public API, cacheable GETs, familiar HTTP tooling, CRUD maps to resources.
“Why add GraphQL?” — Multiple clients, nested reads, reduce endpoint sprawl; accept caching and resolver complexity costs.
“Why gRPC internally?” — Typed contracts, performance, streaming; browsers not direct consumers.
Next: Interview Questions — rehearse answers out loud.