GraphQL and gRPC
Beyond REST — when to use alternatives.
REST is not the only option. GraphQL lets clients ask for exactly the fields they want; gRPC gives schema-first, binary RPC ideal between services. Both shine in specific situations — and both have costs REST doesn't.
The big idea
Three different bets about who pays the complexity cost:
| Attribute | REST | GraphQL / gRPC |
|---|---|---|
| Contract | Resource + URL conventions | Schema-first (`.graphql` / `.proto`) |
| Encoding | JSON | JSON (GQL) / binary (gRPC) |
| Multiple fetches | Multiple round trips | GQL: one query, many fields. gRPC: streams. |
| Best for | Public APIs, browser, simple cases | Varied clients (GQL), internal services (gRPC) |
| Complexity tax | On the client | On the server |
GraphQL: clients ask for what they need
The pitch: one endpoint, one query language, the client chooses fields. No more "5 endpoints for 5 screens."
query HomeScreen($userId: ID!) {
user(id: $userId) {
name
avatar
orders(last: 5) {
id
total_cents
status
items { product { name } quantity }
}
recommendations(limit: 8) {
id
name
price_cents
}
}
}One round trip, the exact shape the screen needs, no over- or under-fetching.
Where GraphQL shines
- Many client types
- Mobile, web, partners all want different fields from the same data.
- Aggregating multiple backends
- A GraphQL gateway fans out to REST/gRPC services and stitches results.
- Evolving APIs
- Adding fields is non-breaking; deprecating fields is built-in.
Where it hurts
gRPC: schema-first RPC over HTTP/2
Define services and messages in a .proto file; generate strongly-typed client and
server stubs in any language.
syntax = "proto3";
service Orders {
rpc Get(GetOrderRequest) returns (Order);
rpc Stream(StreamRequest) returns (stream Order);
rpc Create(CreateOrderRequest) returns (Order);
}
message GetOrderRequest { uint64 id = 1; }
message Order {
uint64 id = 1;
uint64 user_id = 2;
uint32 total_cents = 3;
Status status = 4;
enum Status { NEW = 0; PAID = 1; SHIPPED = 2; CANCELLED = 3; }
}Where gRPC shines
- Service-to-service
- Lower latency than JSON-over-HTTP, contract-checked by the compiler.
- Streaming
- Server-side, client-side, and bi-directional streams are first-class.
- Polyglot internal stacks
- One
.proto, ten language clients generated.
Where it hurts
Picking between them
External / browser-facing API?
Default to REST. Reach for GraphQL when client diversity makes REST endpoint sprawl.
Service-to-service, internal?
Default to gRPC. Schema-first contracts and binary perf are the win.
Many client teams, varied screens?
GraphQL gateway in front of gRPC services is a very common modern shape.
High-throughput streaming?
gRPC streams or a dedicated streaming protocol (WebSockets, SSE).
In practice
The wrong question is "should I use GraphQL/gRPC?" The right question is "what problem am I solving that REST doesn't?" If you can't name one, REST is still the cheapest correct answer.
Key takeaways
- GraphQL lets clients ask for exactly the fields they need — great for varied client types.
- GraphQL costs you HTTP caching and forces you to solve N+1 with batching/DataLoader.
- gRPC is schema-first, binary, and shines between services.
- gRPC streaming is first-class, but browsers need gRPC-Web.
- A common shape: REST or GraphQL at the edge, gRPC between internal services.
Checkpoint questions
Use these to test whether the lesson is clear enough to explain without rereading.
- 1What complexity does GraphQL move from server teams to client teams or schema owners?
- 2Why can GraphQL make caching and N+1 query prevention harder?
- 3What makes gRPC a strong fit for internal service-to-service calls?
- 4Which API style would you choose for browser clients, mobile clients, and internal streaming calls?
References
External resources for going deeper after the lesson above.