When to Consider gRPC
gRPC is not a replacement for REST — it is a tool optimized for specific use cases. Consider migrating when:
- High throughput, low latency — microservice-to-microservice calls where JSON parsing overhead is measurable
- Strongly typed contracts — Protobuf
.protofiles as the source of truth, shared across languages - Bidirectional streaming — chat, real-time analytics, telemetry pipelines
- Polyglot services — multiple languages generated from one
.proto
Keep REST when: you need browser clients, public APIs, simple CRUD endpoints, or your team lacks gRPC experience. gRPC adds complexity — earn it.
REST vs gRPC Comparison
| Attribute | REST | gRPC |
|---|---|---|
| Protocol | HTTP/1.1 or HTTP/2 | HTTP/2 only |
| Payload format | JSON (text) | Protobuf (binary) |
| Schema | OpenAPI (optional) | `.proto` (required) |
| Browser support | Native | grpc-web proxy required |
| Streaming | SSE / WebSocket | Built-in (4 modes) |
| Code generation | Optional | First-class (`protoc`) |
| Payload size | 2–5× larger vs Protobuf | Smallest |
| Latency | Higher (JSON parse) | Lower |
| Observability | Standard HTTP tools | Requires gRPC tooling |
Protobuf serialization is typically 5–10× faster than JSON and produces 60–70% smaller payloads, making it the primary motivation for gRPC in high-throughput microservices.
Protobuf Schema Design
Design .proto schemas carefully — Protobuf is more strict about backward compatibility than JSON.
syntax = "proto3";
package api.v1;
service ProductService {
rpc GetProduct (GetProductRequest) returns (Product);
rpc ListProducts (ListProductsRequest) returns (ListProductsResponse);
rpc CreateProduct (CreateProductRequest) returns (Product);
}
message Product {
int64 id = 1;
string name = 2;
double price = 3;
string category = 4;
// Field 5 deleted — never reuse field numbers!
google.protobuf.Timestamp created_at = 6;
}
Critical rules:
- Never reuse field numbers — old clients still decode deleted fields using the old type. Reserved deleted numbers:
reserved 5; - Never change field types —
int32toint64is wire-incompatible - Use
optionalfor new fields — ensures old servers handle missing fields gracefully - Version your package (
api.v1,api.v2) for major breaking changes
gRPC Gateway (REST + gRPC)
The grpc-gateway project generates a reverse proxy from .proto annotations that exposes your gRPC service as a REST/JSON API:
import "google/api/annotations.proto";
service ProductService {
rpc GetProduct (GetProductRequest) returns (Product) {
option (google.api.http) = {
get: "/v1/products/{id}"
};
};
}
This generates a REST gateway that translates:
GET /v1/products/42 → gRPC GetProduct(id: 42)
The gRPC-Gateway pattern lets you run REST and gRPC simultaneously from the same service — REST for browser clients and public APIs, gRPC for internal microservices.
Service Migration Strategy
Never do a big-bang migration. Use a strangler fig approach:
Phase 1: Add gRPC alongside REST (0% gRPC traffic)
- Implement
.protoschema for the target service - Run gRPC server on port 50051, REST on 8000
- Generate client stubs for internal callers
- Write integration tests for both interfaces
Phase 2: Migrate internal callers (20–80% gRPC traffic)
- Update microservices to use gRPC one by one
- Monitor error rates and latency in parallel
- Roll back per-service if issues arise
Phase 3: Deprecate REST for internal use (100% gRPC internally)
- Keep REST via gRPC-Gateway for external clients
- Add deprecation warnings to REST responses
- Remove internal REST call sites
Error Code Mapping
Map REST HTTP status codes to gRPC status codes systematically:
| HTTP Status | gRPC Status | Code |
|---|---|---|
| 200 OK | OK | 0 |
| 400 Bad Request | INVALID_ARGUMENT | 3 |
| 401 Unauthorized | UNAUTHENTICATED | 16 |
| 403 Forbidden | PERMISSION_DENIED | 7 |
| 404 Not Found | NOT_FOUND | 5 |
| 409 Conflict | ALREADY_EXISTS | 6 |
| 429 Too Many Requests | RESOURCE_EXHAUSTED | 8 |
| 499 Client Closed Request | CANCELLED | 1 |
| 500 Internal Server Error | INTERNAL | 13 |
| 503 Service Unavailable | UNAVAILABLE | 14 |
| 504 Gateway Timeout | DEADLINE_EXCEEDED | 4 |
gRPC also supports rich error details via google.rpc.Status with structured metadata (field violations, retry info, debug messages).
Performance Benchmarks
Measure before and after migration with realistic payloads:
# ghz — gRPC benchmarking tool
ghz --proto api.proto \
--call api.v1.ProductService/ListProducts \
--data '{"page_size": 20}' \
--concurrency 50 \
--total 10000 \
grpc-api.example.com:50051
Typical improvements in microservice benchmarks:
- Throughput: 2–5× higher requests/second
- P99 latency: 30–60% lower
- Payload size: 60–70% smaller
- CPU usage: 10–20% lower (less JSON parsing)
Gradual Rollout
Use feature flags or traffic-splitting to incrementally shift load:
# Python: feature flag controlling gRPC vs REST for a service call
def get_product(product_id: int) -> Product:
if feature_flag('use_grpc_product_service'):
return grpc_client.GetProduct(GetProductRequest(id=product_id))
else:
return rest_client.get(f'/v1/products/{product_id}')
Start with 5% of internal traffic, monitor error rates and latency percentiles, then increase to 25%, 50%, 100% over 1–2 weeks. Keep the REST implementation until gRPC proves stable in production.