The protocol debate is old, and the hot takes get hotter. The reality is boring: each fits a niche; nothing fits everywhere. This post is the working selection guide.
When REST wins
- Public APIs / third-party integrations — every client speaks HTTP/JSON.
- CDN-friendly GET caching (
Cache-Control). - Resource-shaped data (CRUD).
- Tooling: every language, framework, IDE, browser tool understands REST.
GET /users/123
{ "id": 123, "email": "[email protected]" }
For 80% of public APIs: REST is the default. Boring. Fine.
When gRPC wins
- Service-to-service internal calls.
- Strict schemas (protobuf) needed.
- Streaming (server-stream, client-stream, bidirectional).
- Polyglot microservices: codegen for every language.
- Performance at high RPS.
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc StreamEvents(StreamRequest) returns (stream Event);
}
For service mesh / internal RPC: gRPC. See Go gRPC Microservices .
When GraphQL wins
- Clients with varied needs: mobile fetches a slim view; web fetches a rich view; one endpoint serves both.
- Federated APIs: Apollo Federation for combining services.
- Reduce roundtrips: one query for nested data instead of N HTTP calls.
query {
user(id: 123) {
name
posts(limit: 10) {
title
comments { author }
}
}
}
For complex frontends: GraphQL is genuinely useful. For “send/receive a record”: often overkill.
Performance reality
For a typical request from a browser:
- DB query: 5–50ms.
- Business logic: 1–20ms.
- Network RTT: 20–200ms.
- Serialization (JSON vs protobuf): <1ms either way.
Protocol perf differences hide in the noise unless you’re doing heavy compute or high-RPS internal calls.
Schema evolution
| Approach | |
|---|---|
| REST | Versioning (/v1, /v2) or additive changes |
| gRPC | Proto field numbers; backward-compatible by design |
| GraphQL | Deprecation in schema; clients pick fields |
gRPC is the strongest at backward-compat — but only if you follow protobuf rules (don’t reuse field numbers).
Tools
| Strengths | |
|---|---|
| REST + OpenAPI | Mature; codegen for clients/servers; easy testing |
| gRPC + buf | Fast; multi-language; bidi streaming |
| GraphQL + Apollo | Strong tooling; federation; mature |
| tRPC | TypeScript-only; no codegen; great DX |
| Connect | Cross-protocol gRPC / Connect-Web / REST-y |
For TS monoreps: tRPC is shockingly productive — see TypeScript Stack 2026 .
Browser limits
- REST: native fetch; works everywhere.
- gRPC: needs gRPC-Web; loses streaming features; Envoy/proxy needed.
- GraphQL: just POST + JSON; works everywhere.
For browser-direct: REST or GraphQL. gRPC behind a gateway.
Caching
- REST: HTTP cache works. CDN caches GETs. Big perf win for public APIs.
- gRPC: no HTTP cache leverage; cache layer must be application-level.
- GraphQL: every query is a POST. CDN-level caching needs special configuration (Apollo Persisted Queries).
Caching often dominates protocol concerns.
Hybrid is fine
[Browser] ──REST/GraphQL──▶ [Gateway] ──gRPC──▶ [Service A]
──▶ [Service B]
Public API: REST/GraphQL. Internal: gRPC. Each at its strength.
Common mistakes
1. gRPC for public APIs
Third parties hate it. Use REST.
2. GraphQL with no query depth limit
Malicious queries bring down the server. Always limit depth, complexity, and rate.
3. REST without versioning strategy
Breaking changes ship; clients break. Plan v1/v2 or use additive-only changes.
4. Adopting GraphQL without solving N+1
Naive resolvers fan out 100 queries per request. Use DataLoader.
5. Mixing protocols mid-flight
One service half-REST half-gRPC. Pick one per service boundary.
What I’d ship today
For a new app:
- Public API: REST + OpenAPI. Boring; works.
- Internal services: gRPC if multi-language; tRPC if TS-only.
- Mobile-heavy clients: GraphQL is worth it.
- Streaming: gRPC server-streaming or SSE depending on the rest of the stack.
Decision matrix
| Need | Pick |
|---|---|
| Public API for third parties | REST |
| Internal microservices (polyglot) | gRPC |
| TS monorepo full-stack | tRPC |
| Mobile + web with varied views | GraphQL |
| Streaming server → client | gRPC or SSE |
| Real-time bidirectional | gRPC bidi or WebSocket |
Read this next
- Go gRPC Microservices 2026
- API Gateway Patterns 2026
- TypeScript Stack 2026
- FastAPI Streaming and SSE 2026
If you want my hybrid REST+gRPC reference architecture, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .