System Design Bits

Support the project

System Design Bits is free and always will be. If it helped, a coffee helps back.

Buy Me a Coffee QR codeBuy me a coffee

Concept 2 · Communication and APIs·14 min read

API Design: REST, GraphQL, and gRPC

Three ways to expose a system, and the caller each one is built for.

Big Idea

REST, GraphQL, and gRPC are not competing on quality, they’re built for different shapes of problem: resources you fetch and mutate, flexible queries across a graph of data, and fast typed calls between internal services. Picking one is really picking which caller you’re optimizing for.

Why This Exists

The Argument This Concept Ends

▸

A team defaults to REST for everything because it's what they know, then discovers their mobile app makes six round trips to assemble one screen because no single REST endpoint returns exactly what the screen needs.

▸

Another team wires two internal microservices together with hand-rolled JSON over HTTP, then spends real engineering time on serialization bugs and slow parsing that a typed, binary protocol would have avoided from day one.

▸

Without a clear model of what each API style is actually for, engineers argue about which one is 'better' in the abstract, when the real question is always: who is calling this, over what network, and how often does the shape of the request change.

Think Of It Like

Three Ways To Order Food

REST

A fixed menu: you pick item number 7, and you get exactly what’s on the card for item 7, no more, no less, if you want something different you order another item.

GraphQL

A buffet where you build your own plate, naming precisely which sides and how much of each you want in one trip, at the cost of the kitchen having to support arbitrary combinations.

gRPC

Calling the chef directly on a private phone line with a script you both already agree on word for word, extremely fast and precise, but only useful between people who share that script, not something you hand to a random customer off the street.

The Concept

What Each Style Actually Does

REST

How it works

REST models a system as resources, nouns like /users/42 or /orders/17, and uses HTTP verbs (GET, POST, PUT, DELETE) to act on them. The URL structure and status codes carry meaning, which makes REST easy to cache, easy to reason about, and understood by essentially every HTTP client and tool that exists.

The cost

REST's core weakness is fixed response shapes: a single endpoint returns a fixed set of fields, so a client that needs data from three resources either makes three requests or the backend grows a special-purpose endpoint just for that one screen, and that pattern repeats until the API is full of one-off endpoints.

Where it fits

REST fits public and partner-facing APIs where wide compatibility, cacheability, and simplicity matter more than squeezing out the last bit of performance, think a payments API or a public product catalog API that many unrelated clients consume.

GraphQL

How it works

GraphQL exposes a single endpoint backed by a schema describing every type and relationship in the data graph, and the client sends a query naming exactly the fields it wants, nested across relationships, in one round trip. This kills both over-fetching (getting fields you don't need) and under-fetching (needing a second request to get related data).

The cost

GraphQL moves real complexity to the server: resolving a deeply nested query can silently trigger many expensive database calls (the N+1 problem) unless the server batches them deliberately, and a naive GraphQL server can be trivially asked for a query so large it becomes a denial-of-service vector, which means query cost analysis and depth limiting aren't optional extras.

Where it fits

GraphQL fits products with many different client shapes pulling from the same underlying graph of data, like a mobile app and a web app that need different slices of the same user/post/comment graph, and where the frontend team wants to move fast without waiting on backend endpoint changes.

gRPC

How it works

gRPC uses Protocol Buffers, a compact binary format with a strict schema, and HTTP/2 as transport, to call a remote function almost as if it were local: client.getUser(id) instead of assembling a URL and parsing JSON text. Binary encoding and HTTP/2 multiplexing make it substantially faster and lower-bandwidth than JSON-over-HTTP for high-volume service-to-service traffic.

The cost

gRPC's strict schema (the .proto file) is enforced at compile time on both client and server, catching type mismatches before deployment instead of at runtime like a loosely typed JSON API would. The cost is that gRPC is much less friendly to a browser calling it directly and far less human-readable when you're debugging with a plain text tool.

Where it fits

gRPC fits internal service-to-service calls inside one organization's infrastructure, where both ends are services you control, the schema evolves together, and call volume and latency actually matter, not calls made by an arbitrary third-party developer.

Key Terms

The Vocabulary Of An API Contract

Resource

In REST, a noun the API exposes and lets you act on with HTTP verbs, like a user, an order, or a post, identified by a stable URL.

Over-fetching

Getting more fields in a response than the client actually needs, wasting bandwidth and parsing time, a common REST symptom when one endpoint serves many different callers.

Under-fetching

Not getting enough data in one response, forcing the client to make additional requests to assemble what it needed, another common REST symptom that GraphQL is specifically designed to remove.

N+1 problem

A performance bug, common in naive GraphQL resolvers, where fetching a list of N items triggers N additional individual queries for related data instead of one batched query.

Protocol Buffers (protobuf)

The binary, strictly typed serialization format gRPC uses, defined in a .proto schema file shared by client and server, compiled into native code for each language.

Schema-first API

An API design approach where the contract (types, fields, operations) is defined explicitly up front, as GraphQL and gRPC both require, catching mismatches before runtime rather than after.

Worked Example

Fetching A User’s Profile With Their Last 3 Posts

REST
●

GET /users/42 returns the user

●

GET /users/42/posts?limit=3 returns the posts, a second round trip

●

Response shape is fixed by the server, client takes what it's given

VS
GraphQL
●

One query asks for user { name, posts(limit: 3) { title } }

●

One round trip returns exactly those fields, nothing more

●

Server resolves the nested relationship internally

The Shape Of It

Where Each Style Typically Sits In One System

Where each API style sits in one systemGraphQL queryGraphQL querygRPC callgRPC callHTTP GET/POSTCLIENTMobile appCLIENTWeb appGATEWAYGraphQL gatewaySERVERUser serviceSERVERPost serviceCLIENTPartner integrationSERVERPublic REST API
Notice this system uses all three. The gateway speaks GraphQL outward because client shapes vary, gRPC inward because both ends are services the same team controls, and the partner path stays REST because a third party has to integrate with it.

Putting It Together

Which API Style Fits Your Caller?

Which API style fits your calleryesnoyes, many apps/screensno, fixed resourcesWho is calling this API?Internal serviceyou control?Do client shapesvary a lot?gRPC: fast, typed, binaryGraphQL: client picks fieldsREST: cacheable, universal

Seen In The Wild

Who Picked What, And Why

▪

Facebook created GraphQL specifically to solve mobile over-fetching and under-fetching across News Feed, Marketplace, and other products that all pull from the same enormous social graph with very different client needs.

▪

Stripe's public payments API is REST, deliberately, because millions of unrelated developers integrate with it and REST's ubiquity, cacheability, and simple mental model matter far more than shaving milliseconds off a request.

▪

Google, which invented gRPC, uses it pervasively for internal service-to-service communication across its own infrastructure, where every caller and callee is a service Google itself controls and both ends can be updated together.

▪

Netflix uses gRPC extensively between its hundreds of internal microservices for exactly the same reason: high call volume, controlled endpoints, and a strong need for low latency and strict typing.

Key Points

What To Carry Forward

✓

REST models resources with HTTP verbs, is cacheable and universally understood, but suffers from fixed response shapes that cause over-fetching and under-fetching.

✓

GraphQL lets the client ask for exactly the fields it needs across a graph of relationships in a single round trip, at the cost of server-side complexity like the N+1 problem and query cost limiting.

✓

gRPC uses binary Protocol Buffers over HTTP/2 for fast, strictly typed calls, ideal between services you control, but is a poor fit for public or browser-facing APIs.

✓

The right choice depends on the caller: public/partner APIs lean REST, many-shaped frontend clients lean GraphQL, internal service-to-service calls lean gRPC.

✓

Schema-first designs (GraphQL, gRPC) catch mismatches before runtime; REST's looser contract trades that safety for simplicity and reach.

✓

Real systems often use more than one style at once, a GraphQL gateway in front of internal gRPC services is a common combination, not a contradiction.

Common Mistakes

Where This Usually Goes Wrong

✕

Treating this as a single 'best API style' debate instead of matching the style to who is calling the API and how often the required data shape changes.

✕

Adopting GraphQL without building query depth limits or cost analysis, leaving the server open to a single expensive nested query taking down the database.

✕

Using gRPC for a public-facing API that third-party developers or browsers need to call directly, ignoring that most browsers and casual integrators can't easily consume it.

✕

Designing a REST API with dozens of screen-specific endpoints to work around fixed response shapes, when that pain is exactly the signal that GraphQL might fit better.

✕

Assuming GraphQL is automatically faster than REST, when a poorly resolved GraphQL query can be far slower than a well-cached REST endpoint for the same data.

“One bark is water. Two is outside. A long stare is treat. My human and I have never needed words. Then the dog-sitter arrived, and I barked twice at her for an hour. Turns out a private language is only fast if both ends already speak it.”

Try It Yourself

Count The Round Trips

Pick a screen in an app you use that clearly combines data from more than one resource, like a profile page showing a user plus their recent activity. Sketch what the REST version would need (how many endpoints, how many round trips) versus a single GraphQL query for the same screen, and name one reason an internal team building that same feature’s backend services might use gRPC between them instead of either.

Quiz Review

Check your understanding

Question 1 of 6

Why doesn't GraphQL simply replace REST everywhere, given that it removes over-fetching and under-fetching?

  • ✓GraphQL trades those wins for real costs: a single flexible endpoint is harder to cache with standard HTTP caching, harder to rate-limit sensibly since queries vary in cost, and requires server-side protections against expensive nested queries that a fixed-shape REST endpoint never needs. For simple, cacheable, widely-consumed public APIs, REST's constraints are often a feature, not a limitation.