REST vs gRPC
Choose API protocol by ecosystem fit, latency targets, and contract strictness.
Problem it solves
- Microservices need a contract for how they communicate — ad-hoc JSON leads to breaking changes and integration bugs.
- Internal high-frequency calls suffer from JSON parsing overhead and lack of streaming primitives.
- Public APIs must work with browsers, mobile apps, third-party developers, and standard HTTP tooling.
- Teams need to pick a protocol that matches client ecosystem, latency budget, and evolution strategy.
Definition
REST models APIs as resources (/users/42) manipulated by HTTP verbs (GET, POST, PUT, DELETE) with JSON payloads. gRPC models APIs as service methods (GetUser, StreamUpdates) defined in a .proto IDL, serialized with Protocol Buffers over HTTP/2, supporting unary and streaming RPC modes.
Why It Matters
- Wrong protocol choice increases latency, payload size, and integration friction.
- gRPC code generation from
.protocatches contract mismatches at compile time. - REST's ubiquity makes it the default for public APIs and CDN/cache layers.
- Hybrid architectures (REST gateway → gRPC backend) are the industry norm at scale.
Visual
REST (HTTP/1.1 or HTTP/2 + JSON): resource URLs, verbs, human-readable browser-native, cache/proxy ecosystem gRPC (HTTP/2 + Protobuf): service/method RPC, strongly typed .proto binary payload, low latency, streaming Hybrid (common in production): REST/JSON at edge gateway -> gRPC inside service mesh
When to Use
- REST for public APIs, browser/mobile clients, webhooks, and anything needing HTTP caching/CDN.
- gRPC for internal microservice calls requiring low latency, typed contracts, and bidirectional streaming.
- gRPC streaming for real-time feeds, log tailing, or chat-style push between services.
- REST + OpenAPI when API discoverability and third-party developer experience matter most.
When NOT to Use
- gRPC directly from browser without gRPC-Web + proxy/transcoding layer.
- REST/JSON for ultra-high-frequency internal calls where protobuf size/speed gains are significant.
- gRPC when clients cannot generate stubs from
.proto(legacy systems, quick scripts). - Replacing all REST with gRPC without a gateway strategy for external consumers.
Code Variations
syntax = "proto3";
package user.v1;
service UserService {
rpc GetUser(GetUserRequest) returns (UserResponse);
rpc ListUsers(ListUsersRequest) returns (stream UserResponse);
rpc WatchUser(WatchUserRequest) returns (stream UserEvent);
}
message GetUserRequest { string id = 1; }
message UserResponse { string id = 1; string name = 2; }.proto. Field numbers must never be reused.@Override
public void getUser(GetUserRequest req,
StreamObserver<UserResponse> observer) {
User u = userRepo.findById(req.getId()).orElseThrow();
observer.onNext(toProto(u));
observer.onCompleted();
}onNext multiple times before onCompleted.GET /api/v1/users/{id} -> GetUser
GET /api/v1/users?page=1&size=20 -> ListUsers (paginated)
SSE /api/v1/users/{id}/events -> WatchUser (streaming via SSE)Choose REST when:
- Browser/mobile public API
- Third-party developers need curl/Postman
- HTTP caching/CDN matters
Choose gRPC when:
- Internal service-to-service (high QPS)
- Strong typing + codegen valued
- Streaming RPC needed
Hybrid when:
- Both external REST and internal gRPC
- Use gateway for transcodingREST vs gRPC — Side-by-Side
| Aspect | REST | gRPC |
|---|---|---|
| Transport | HTTP/1.1 or HTTP/2 | HTTP/2 (required) |
| Payload format | JSON (human-readable, verbose) | Protobuf (binary, compact) |
| Contract | OpenAPI/Swagger (optional, runtime validation) | .proto IDL (required, compile-time) |
| API style | Resource-oriented (nouns + verbs) | RPC-oriented (service methods) |
| Code generation | Optional (OpenAPI generators) | Built-in (stubs for many languages) |
| Streaming | SSE, WebSocket, chunked (extra patterns) | Unary, server, client, bidirectional streaming (native) |
| Browser support | Native fetch/XHR | Needs gRPC-Web + Envoy/grpc-web proxy |
| Caching | HTTP cache headers, CDN friendly | Not cache-friendly by default |
| Error model | HTTP status codes + JSON body | gRPC status codes + trailing metadata |
| Typical latency | Higher (JSON parse + larger payload) | Lower (binary + HTTP/2 multiplexing) |
| Best fit | Public APIs, BFF, third-party integrations | Internal service mesh, high-throughput RPC |
Key trade-offs
| Area | REST advantage | gRPC advantage | Typical resolution |
|---|---|---|---|
| Interoperability | Works everywhere (curl, Postman, browser) | Needs tooling/codegen | REST at public edge |
| Performance | Good enough for most APIs | 2–10x smaller payloads, faster parse | gRPC for hot internal paths |
| Contract evolution | Flexible; optional fields common | Strict; field numbers, backward compat rules | Both need versioning discipline |
| Debugging | Human-readable JSON in logs | Binary — needs grpcurl/protobuf decode | REST for external; gRPC + observability inside |
| Load balancing | L7 HTTP LB (path/host based) | L4 or gRPC-aware LB (needs connection reuse awareness) | Service mesh (Istio/Linkerd) for gRPC |
Common Confusions Cleared Up
REST thinks in resources and representations (GET /orders/42). gRPC thinks in procedures (OrderService.GetOrder(id=42)). Neither is universally superior — match to client mental model and tooling.
REST APIs often break silently when a field is renamed — clients parse JSON defensively. gRPC .proto changes require regeneration; incompatible changes fail at build time if teams enforce proto review.
gRPC over HTTP/2 multiplexes many RPCs on one connection — reduces connection overhead for chatty services. REST over HTTP/1.1 opens many connections; HTTP/2 REST gets partial benefit without protobuf gains.
Variations / Implementations
1) REST-only (startup / public API)
Single REST API with OpenAPI spec. Simplest stack. Works for monoliths, MVPs, and public developer platforms.
2) gRPC internal + REST gateway (hybrid)
Services talk gRPC internally. API gateway (Envoy, Kong, AWS API Gateway) transcribes REST/JSON ↔ gRPC for external clients. Most common microservices pattern at scale.
3) gRPC streaming for realtime
Server streaming RPC pushes updates (StreamStockPrices). Client streaming for bulk uploads. Bidirectional for chat/collab — without WebSocket boilerplate.
Common Mistakes / Anti-Patterns
- gRPC from browser without gRPC-Web proxy.
- Breaking
.protofield numbers or reusing tag numbers. - Giant JSON payloads on hot internal paths when gRPC would cut bandwidth 5x.
- No API versioning strategy (REST
/v1/or proto package versioning). - Ignoring gRPC load balancing — sticky connections to wrong backend.
- Replacing REST entirely without considering external client ecosystem.
Interview Q&A
Likely Follow-up Questions
- What is gRPC-Web and when do you need it?
- How does a service mesh help with gRPC load balancing?
- REST vs GraphQL — where does each fit?