REST vs GraphQL vs gRPC
In the last lesson we established that the API is the contract between client and server. But there's no single way to design that contract. Three approaches dominate production systems today: REST, GraphQL, and gRPC. Each solves the same problem — get data from A to B — with very different tradeoffs in performance, flexibility, and complexity.
API Styles: Request & Response
REST
REST (Representational State Transfer) is the most widely used API style on the internet. It treats everything as a resource identified by a URL, and uses standard HTTP methods to act on it.
- GET — read a resource
- POST — create a resource
- PUT / PATCH — update a resource
- DELETE — remove a resource
A REST API for a user service might look like this: GET /users/123 returns the user, POST /users creates one, DELETE /users/123 removes it. The structure is predictable and immediately familiar to any developer.
REST is stateless — each request carries everything the server needs to process it. No session memory on the server side. This makes REST servers easy to scale horizontally.
The problem: over-fetching and under-fetching.
When you fetch GET /users/123, the server returns the entire user object — id, name, email, address, phone number, avatar URL, preferences. But your mobile app only needed the name and avatar. You fetched more than you needed. That's over-fetching.
The reverse is under-fetching. To render a profile page you need the user, their recent posts, and their follower count. With REST that's three separate requests: GET /users/123, GET /users/123/posts, GET /users/123/followers. Three round trips for one screen.
GraphQL
GraphQL was built at Facebook in 2012 to solve exactly the problems REST has with complex frontends. Instead of many endpoints, there is a single endpoint — usually /graphql — and the client sends a query that describes exactly what it needs.
query {
user(id: "123") {
name
avatar
recentPosts {
title
publishedAt
}
}
}
The server responds with exactly those fields. No more, no less. One request, one round trip, zero wasted data. This is especially valuable on mobile where bandwidth matters.
The tradeoffs. GraphQL adds significant complexity on the server side. You define a schema, write resolvers for every field, and manage a type system. Caching is harder — because every query is unique, you can't use simple HTTP caching the way you can with REST. Tooling has matured a lot (Apollo, Relay, Hasura) but the operational overhead is real.
gRPC
gRPC was built by Google and takes a completely different approach. Where REST and GraphQL use JSON over HTTP, gRPC uses Protocol Buffers (protobuf) — a binary serialization format — over HTTP/2.
You define your service and data structures in a .proto file:
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
message UserRequest {
string id = 1;
}
message UserResponse {
string name = 1;
string email = 2;
}
The tooling generates client and server code in your language of choice. You call the remote procedure like a local function.
The results are dramatic. Protobuf messages are typically 5–10x smaller than equivalent JSON. Serialization and deserialization are significantly faster. HTTP/2 enables multiplexing — many concurrent requests over a single connection with no head-of-line blocking.
The catch: gRPC is not browser-native. Browsers don't support raw HTTP/2 at the level gRPC requires. You need a proxy (gRPC-Web) to expose gRPC services to browsers, and the developer experience is more complex. Binary payloads aren't human-readable, which makes debugging harder. gRPC shines in backend-to-backend communication where performance is critical.
Comparison
| REST | GraphQL | gRPC | |
|---|---|---|---|
| Use case | Public APIs, CRUD | Complex frontends, mobile | Internal microservices |
| Payload format | JSON | JSON | Binary (Protobuf) |
| Performance | Good | Good | Excellent |
| Caching | Easy (HTTP cache) | Complex | Not applicable |
| Browser support | Full | Full | Limited (needs proxy) |
| Complexity | Low | Medium | High |
| Human readable | Yes | Yes | No |
When to Use Each
Use REST when:
- You're building a public API that third parties will consume
- Your data model maps naturally to resources (users, posts, orders)
- You want maximum compatibility and minimal client-side setup
- HTTP caching matters to your architecture
Use GraphQL when:
- You have a complex frontend with many different data requirements
- Mobile clients need to minimize bandwidth and round trips
- Multiple clients (web, iOS, Android) need different shapes of the same data
- You want a strongly typed schema as the contract between teams
Use gRPC when:
- You're building internal service-to-service communication
- Performance and payload size are critical constraints
- You're running a microservices architecture where services talk to each other constantly
- You control both the client and server (polyglot environments with generated clients)
Key Takeaways
- REST is simple, stateless, and universal — the right default for most public APIs
- GraphQL lets clients ask for exactly what they need — powerful for complex frontends, expensive to operate
- gRPC is the fastest option but sacrifices human readability and browser compatibility — built for microservices
- Most large systems use all three: REST or GraphQL for the public-facing layer, gRPC internally between services
What's Next
Now that you understand how data travels between services, the next question is where it lives. In the next lesson we'll look at databases — specifically the SQL vs NoSQL decision that every system design requires you to make.
Enjoyed this breakdown?
Get new lessons in your inbox.