Blog 10
API Design — REST, GraphQL, and gRPC Trade-offs
Three tools, three different jobs
REST, GraphQL, and gRPC get compared as if one is simply better than the others. In practice, they're optimized for different situations, and the "best" API style for a public-facing e-commerce platform is often the wrong choice for internal microservice communication within the same company.
REST: resource-oriented, cache-friendly, universally understood
REST models an API as a set of resources (/users/123, /orders/456) manipulated through standard HTTP verbs (GET, POST, PUT, DELETE). Its biggest strengths:
- HTTP caching works for free. A
GET /products/789response can be cached by browsers, CDNs, and intermediate proxies using standard HTTP cache headers, with zero custom infrastructure. This is a huge, often underrated advantage for read-heavy public APIs. - Universal client support. Every language, every platform, every tool understands HTTP and JSON. There's no special client library requirement.
- Simple mental model. Resources and verbs map naturally to how most people think about data.
Its main weakness is fetch granularity mismatch. A mobile client on a slow connection might need only a user's name and avatar, but a REST endpoint designed for a desktop dashboard might return every field on the user object — that's over-fetching. Conversely, rendering a page that needs a user, their orders, and each order's line items might require three or four separate REST round-trips — that's under-fetching, solved with extra requests that each carry their own latency cost.
GraphQL: client-specified shape, single round-trip
GraphQL flips the fetch-granularity problem around: instead of the server deciding what shape each endpoint returns, the client specifies exactly which fields it needs in a single query, and the server returns exactly that shape — no more, no less, in one round-trip regardless of how many underlying resources are involved.
This is a genuinely strong fit when:
- You have multiple, diverse client types (web app, iOS app, Android app, third-party integrations) that each need different slices of the same underlying data. Without GraphQL, you'd either over-fetch on the leanest client or build bespoke REST endpoints per client — both bad options at scale.
- Your UI is deeply nested and relational — a single screen needs a user, their recent orders, each order's items, and each item's product details. One GraphQL query replaces what would be several sequential REST calls.
The trade-offs are real, though:
- HTTP caching mostly stops working. Because GraphQL typically uses a single
POST /graphqlendpoint with a query in the body, the standard HTTP caching layer (which keys on URL) can't help you — caching has to be reimplemented at the application or field level. - Query complexity becomes a server-side risk. A client can, intentionally or not, request a deeply nested query that fans out into an expensive number of underlying database calls — this needs to be actively guarded against (depth limiting, query cost analysis), or a client can accidentally overload the backend.
- More backend infrastructure to build and maintain — resolvers, schema, and often a dataloader/batching layer to avoid the N+1 query problem that naive resolver implementations fall into immediately.
gRPC: fast, typed, internal service-to-service communication
gRPC uses Protocol Buffers (a compact binary serialization format) over HTTP/2, and is built for a different use case entirely than public-facing REST or GraphQL APIs: internal communication between services you control, where performance and strict contracts matter more than human readability.
Its strengths:
- Performance. Binary serialization is smaller and faster to parse than JSON, and HTTP/2 allows multiplexed streaming over a single connection — meaningful at high internal request volumes.
- Strict, generated contracts. The
.protofile defines the exact shape of every request and response, and client/server code is generated from it — this eliminates an entire category of "the API docs are out of date" bugs that plague loosely-typed REST APIs. - Native streaming support. gRPC supports client-streaming, server-streaming, and bidirectional streaming as first-class concepts, which REST has to bolt on awkwardly (long polling, Server-Sent Events, WebSockets as separate mechanisms).
Its weaknesses are exactly why it's a poor fit for public APIs:
- Not browser-native. Browsers can't easily speak raw gRPC; it requires a proxy layer (like gRPC-Web) to bridge to browser clients, adding infrastructure.
- Binary format isn't human-debuggable. You can't just open dev tools and read a gRPC payload the way you can read JSON — this makes ad-hoc debugging and third-party integration meaningfully harder.
- Steeper adoption cost for external partners — asking a third-party integrator to consume gRPC is a much bigger ask than handing them a REST endpoint and a Postman collection.
A decision heuristic
Ask three questions:
- Who calls this API? The public internet and third parties → REST (universally consumable). Internal services you control → gRPC is on the table. Multiple diverse first-party clients needing different data shapes → GraphQL is on the table.
- Does response shape vary significantly by client? No, mostly uniform needs → REST is simplest. Yes, meaningfully different per client → GraphQL earns its complexity.
- Does raw performance and strict typing matter more than human-readability and universal compatibility? Yes, and it's internal-only → gRPC. No, or it needs to be broadly consumable → REST or GraphQL.
It's also entirely normal — and common in real systems — to use more than one of these simultaneously: a public-facing REST or GraphQL API at the edge, with gRPC used for the internal service-to-service calls that fulfill it. The choice isn't "pick one for the whole company," it's "pick the right one for this specific boundary."
This closes out the Core Building Blocks phase of the series. Next: Architecture Decision Deep Dives, starting with rate limiting — a topic that touches almost every API style covered here, regardless of which one you chose.