gRPC: Contract Layer, Transport and Serialization
3.1 Contract Layer
Protobuf Schema
Protocol Buffers (protobuf) is the default serialization format for gRPC.
You define messages and services in .proto files. The compiler generates code for your language.
syntax = "proto3";
message User {
string id = 1;
string name = 2;
string email = 3;
int32 age = 4;
}
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
Field Numbering
Each field has a unique number. These numbers identify fields in binary format.
Rules:
- Numbers 1–15 use 1 byte — use them for frequent fields
- Numbers 16–2047 use 2 bytes
- Never reuse deleted field numbers
- Use the reserved keyword for removed fields
message User {
reserved 4, 8;
reserved "old_field_name";
string id = 1;
string name = 2;
}
Backward Compatibility
Adding new fields is safe. Old clients simply ignore unknown fields.
Removing fields — mark as reserved. Never change a field's type or number.
Forward Compatibility
Old code can read new messages. Unknown fields are preserved during deserialization in proto3. This means a proxy service can forward messages it does not fully understand.
Advanced Field Types
oneof— only one field in the group can be set. Like a union/variant type.oneof payment { CreditCard card = 5; BankTransfer transfer = 6; }map— key-value pairs. Keys must be string or integer types.map<string, string> metadata = 7;- Well-known types — Google's standard messages for common patterns:
google.protobuf.Timestamp— date/time (seconds + nanos since epoch)google.protobuf.Duration— time spangoogle.protobuf.Struct— untyped JSON-like structure (use sparingly)
3.2 Transport
HTTP/2 Multiplexing
gRPC uses HTTP/2 as its transport protocol. Multiple RPC calls share one TCP connection. Each call is an independent stream. No head-of-line blocking between streams — one slow response does not block others.
Flow Control
HTTP/2 has built-in flow control at the stream level. The receiver tells the sender how much data it can accept (window size). This prevents a fast sender from overwhelming a slow receiver.
Flow control works per-stream and per-connection. gRPC manages it automatically, but you can tune window sizes for high-throughput scenarios.
Connection Reuse
One TCP connection serves many RPCs at the same time. This reduces TCP handshake overhead (no new connection per request).
Best practices: - Use keepalive pings to maintain idle connections - Set keepalive interval (e.g., 30 seconds) and timeout - Use connection pooling for high-load services - Monitor connection count — too many connections wastes resources
Headers and Trailers
gRPC uses HTTP/2 headers for metadata (auth tokens, request IDs). Status codes are sent in trailers — after the response body.
This is different from REST where status code comes first. In gRPC, the server can start sending data before it knows the final status.
3.3 Serialization
Binary Encoding
Protobuf encodes data in compact binary format. Field numbers + wire types replace field names. This makes the payload much smaller than JSON.
Wire types: - 0: Varint (int32, int64, bool, enum) - 1: 64-bit (fixed64, double) - 2: Length-delimited (string, bytes, embedded messages) - 5: 32-bit (fixed32, float)
Payload Efficiency
Protobuf is 3–10x smaller than JSON for the same data. Serialization and deserialization are faster because: - No field name parsing - No text-to-number conversion - Schema-driven encoding (compiler knows types)
Example — a user object with id, name, email: - JSON: ~120 bytes - Protobuf: ~40 bytes
Comparison with JSON
| Aspect | Protobuf | JSON |
|---|---|---|
| Format | Binary | Text |
| Size | Small (3–10x less) | Large |
| Speed | Fast | Slower |
| Human-readable | No | Yes |
| Schema | Required (.proto) | Optional (OpenAPI) |
| Type safety | Strong (compile-time) | Weak (runtime) |
| Browser support | Limited (grpc-web) | Native |
| Debugging | Need tools (grpcurl) | curl / browser |
When Protobuf Wins
- Service-to-service communication (microservices)
- High-throughput pipelines
- Mobile clients with limited bandwidth
- Strict type contracts between teams
When JSON Wins
- Public APIs consumed by browsers
- Quick prototyping and debugging
- Human-readable logs and configs
- Loose coupling between teams