gRPC Metadata vs Proto Fields for HTTP Headers
The Point
When converting a partner’s HTTP API to proto, standard HTTP headers (Content-Length, etc.) should not go into the message – the gRPC framework handles them automatically. Custom headers (like amzn-request-id) go either in a response message field or in gRPC trailing metadata, depending on their purpose.
Explanation
Background: converting an Excel API spec to proto
The partner’s API doc (Japanese-style Excel format) lists HTTP response headers and custom headers together. When converting to proto, you need to decide which ones belong in the message and which do not.
Standard HTTP Headers – do not put in proto
Transport-layer headers like Content-Length, Content-Type, and Transfer-Encoding are handled by the gRPC framework:
Content-Typeis alwaysapplication/grpcContent-Lengthis determined by serialized message size, filled in automatically by the framework- Putting them in proto only causes semantic confusion (is this a business field or an HTTP header?)
Custom Headers (e.g. amzn-request-id) – two approaches
| Approach | Pros | Cons | Best for |
|---|---|---|---|
| Response message field | Simple, consumer reads directly | Header semantics flattened into message | IDs with business/tracing meaning |
| gRPC Trailing Metadata | Follows gRPC idiom, does not pollute message | Consumer needs to use the metadata API | Pure infra use (rate limit, etc.) |
Go server-side trailing metadata example
1import "google.golang.org/grpc/metadata"
2
3func (s *Server) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.GetOrderResponse, error) {
4 // ... call partner API ...
5
6 // Put amzn-request-id in trailing metadata
7 trailer := metadata.Pairs("amzn-request-id", partnerResp.Header.Get("amzn-request-id"))
8 grpc.SetTrailer(ctx, trailer)
9
10 return &pb.GetOrderResponse{Order: order}, nil
11}Add proto comments to preserve traceability
Even if you do not put all headers into the message, you can document the mapping in proto comments for future reference:
1message GetOrderResponse {
2 // Maps to amzn-request-id response header in partner API.
3 // Passed as gRPC trailing metadata, not as a message field.
4 Order order = 1;
5}
6
7// Note: Standard HTTP headers (Content-Length, Content-Type) are intentionally
8// omitted — handled by gRPC framework automatically.Knowledge Sugar
The “shallow mapping first” logic from a colleague
Map all fields first for traceability, then prune later. This makes sense from a traceability perspective, but once a proto is published there is a risk of breaking changes. A better compromise is to document the mapping in comments rather than putting every header into the message.
What is gRPC Metadata?
gRPC metadata is a set of key-value pairs, equivalent to HTTP headers in the gRPC world. It comes in two flavors:
- Header metadata: sent before the response starts (
grpc.SendHeader) - Trailing metadata: sent after the response ends (
grpc.SetTrailer), more commonly used for carrying additional information