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
The Concept
What Each Style Actually Does
REST
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.
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.
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
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).
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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
The Shape Of It
Where Each Style Typically Sits In One System
Putting It Together
Which API Style Fits Your Caller?
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
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.