system design

Choosing REST, gRPC, or GraphQL in a system design interview

The question usually lands right after you draw an arrow between two boxes on the whiteboard. The interviewer taps the arrow and asks what it actually carries. If your answer is “an API” and you stop there, you’ve left the most interesting decision in the whole design on the table. REST, gRPC, and GraphQL are three different answers to “how do these services talk,” and picking the wrong one for the scenario is the kind of thing a good interviewer will happily chase you around for ten minutes.

So it helps to know what each one is really optimizing for, and to have a defensible reason ready before you’re asked.

What each style actually is

REST models everything as a resource you address by URL. You GET /users/42, you POST /users, you DELETE /orders/9. The verbs come from HTTP, the data usually comes back as JSON, and every response is a full representation of the thing you asked for. It’s the boring default, and boring is frequently the correct call.

GraphQL flips the control. Instead of the server deciding what a /users/42 response contains, the client sends a query describing the exact fields it wants, and the server assembles precisely that shape. There’s one endpoint, usually POST /graphql, and the query itself carries all the specificity. A mobile screen that needs a name and an avatar asks for two fields; a dashboard that needs fifty asks for fifty, against the same backend, with no new endpoint on either side.

gRPC is remote procedure calls done seriously. You define your service and message types in a .proto file, a code generator produces client and server stubs in whatever languages you use, and calls travel as compact binary Protocol Buffers over HTTP/2. You’re not thinking in resources or queries, you’re calling GetUser(UserRequest) as if it were a local function that happens to cross the network.

A quick worked example

Say you need a user’s name plus their last three orders. With REST you probably hit GET /users/42 and then GET /users/42/orders?limit=3, two round trips, and the first response hands you a dozen fields you didn’t want. With GraphQL you send one query naming user { name orders(last: 3) { total } } and get back exactly those fields in a single request. With gRPC you’d expose a method like GetUserWithOrders, define the response message once in the proto, and both sides get typed structs for free, no hand-written JSON parsing.

The tradeoffs an interviewer will push on

Caching is where REST quietly wins and candidates forget to mention it. Because a REST GET is a plain HTTP request against a stable URL, every layer between client and server already knows how to cache it: the browser, a CDN, a reverse proxy, all of it, using Cache-Control and ETag headers that have worked for two decades. GraphQL sends everything as a POST to one URL, so that free HTTP caching evaporates and you rebuild it at the application layer with persisted queries or a client cache like Apollo. gRPC has the same problem for the same reason. If your read traffic is huge and cacheable, that’s a genuine point for REST, and saying so out loud signals you’ve actually run one of these in production.

Over-fetching and under-fetching is the pain GraphQL exists to kill. A REST endpoint returns a fixed shape, so a lean mobile client either downloads fields it throws away or fires several requests to stitch one screen together. That’s the classic chatty-client problem. GraphQL collapses it into a single round trip with no wasted bytes. The catch is that it moves the cost server-side: one innocent-looking query can fan out into hundreds of database hits, the N+1 problem, and you’ll be expected to name the fix, which is batching with something like DataLoader so those child lookups collapse into one query per level.

Raw performance on the wire is gRPC’s home turf. Protocol Buffers serialize to a fraction of the size of the equivalent JSON, and HTTP/2 lets many calls share one connection through multiplexing instead of opening a socket per request. For east-west traffic, the service-to-service calls inside your own cluster where every millisecond of tail latency compounds across a call chain, that efficiency adds up. gRPC also treats streaming as a native feature, including bidirectional streams, which is awkward to bolt onto REST.

Then there’s who has to consume the thing. gRPC doesn’t run natively in a browser, you need a grpc-web proxy layer to translate, so a public API that random external developers call from JavaScript is a poor fit. Those same developers can hit a REST endpoint with curl and read the JSON without generating a single stub. REST’s ubiquity is the feature. That’s why almost every public API you’ve integrated against, Stripe, GitHub, Twilio, is REST or REST-ish rather than gRPC.

How to actually choose in the room

The answer interviewers want is rarely “pick one for everything.” It’s matching the style to the boundary you’re standing at. An external-facing API that third parties call, or anything that benefits from HTTP caching and broad tooling, points to REST. Internal service-to-service calls where latency and strict contracts matter more than human readability point to gRPC. A single frontend, or a family of them, that needs many different slices of the same data without a new endpoint per screen points to GraphQL, often sitting as a gateway in front of services that themselves talk gRPC to each other.

That last combination is worth saying explicitly, because it shows you don’t treat these as rivals. A very common real architecture puts GraphQL at the edge for the mobile and web clients, gRPC behind it between the microservices, and a few REST endpoints hanging around for webhooks and third-party integrations. Naming that hybrid usually beats defending a single winner.

Dimension REST gRPC GraphQL
Data format JSON text over HTTP/1.1 Binary Protocol Buffers over HTTP/2 JSON, client-specified query shape
Contract Loose (OpenAPI optional) Strict, generated from .proto Strongly typed schema
HTTP caching Native (GET, ETag, CDN) Not out of the box Not out of the box
Over/under-fetching Common, response shape is fixed Fixed per method Eliminated, client picks fields
Browser support Everywhere Needs grpc-web proxy Everywhere
Streaming Awkward (SSE or long-poll) Native, bidirectional Subscriptions over WebSocket
Strongest fit Public APIs, cacheable reads Internal microservices Varied client data needs

Questions phrased the way they’re actually asked

The topic rarely arrives as “compare these three.” It’s embedded in a larger design, and the API choice is a branch off it. Some of the ways it surfaces:

  • “We’re designing the backend for a mobile app that’s slow on 3G. How would you cut the number of requests per screen?”
  • “Two internal services call each other a million times a minute. What’s carrying that traffic and why?”
  • “You picked GraphQL. A single query is now hammering the database. What happened, and how do you fix it?”
  • “Your public API needs to survive a traffic spike from a front-page link. What in your API choice helps you here?”

Each one is really testing whether you connect the protocol to a concrete constraint: request count, internal throughput, the N+1 trap, cacheability. The name of the tool is the easy part.

The trap to avoid is treating this as a popularity contest, defending gRPC because it’s fast or GraphQL because it’s flexible without tying the choice to the scenario in front of you. gRPC’s speed does nothing for a public API that browsers call. GraphQL’s flexibility is dead weight if every client wants the same three fields. An interviewer isn’t checking whether you’ve read the comparison blog posts. They’re checking whether you’d make the same call under real constraints, and whether you can say plainly what you’d give up to get there.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

1972 Soviet postage stamp commemorating the Mars 2 probe

worth a read

Mars For The Rest of Us — a weekly-or-more deep dive on the technical side of Mars exploration: rocket propulsion, microbiology, mission architecture, and everything in between. Written by Maciej Ceglowski.

Read it on Substack →
Scroll to Top