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
RESTVersioning (/v1, /v2) or additive changes
gRPCProto field numbers; backward-compatible by design
GraphQLDeprecation 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 + OpenAPIMature; codegen for clients/servers; easy testing
gRPC + bufFast; multi-language; bidi streaming
GraphQL + ApolloStrong tooling; federation; mature
tRPCTypeScript-only; no codegen; great DX
ConnectCross-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

NeedPick
Public API for third partiesREST
Internal microservices (polyglot)gRPC
TS monorepo full-stacktRPC
Mobile + web with varied viewsGraphQL
Streaming server → clientgRPC or SSE
Real-time bidirectionalgRPC bidi or WebSocket

Read this next

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 .