With gRPC transcoding, how does a google.api.http annotation expose one gRPC method as an HTTP/JSON endpoint, and what does the JSON face lose?
answer
- one service, two faces
- HttpRule on the method
- verb, URL template, body
- path variables bind request fields
- streams, trailers, rich status
basics
~20 sA google.api.http HttpRule names an HTTP verb and a URL template whose path variables bind request fields, plus a body mapping; a transcoder converts HTTP/JSON to the gRPC call. The JSON face loses streaming, trailer metadata and typed error fidelity.
solid answer
~50 sgRPC transcoding is driven by the `google.api.http` option on an rpc, whose value is an `HttpRule`. The rule holds one pattern - `get`, `put`, `post`, `delete`, `patch`, or `custom` for another verb - with a URL template such as `/v1/{name=orders/*}:cancel`, where `{...}` variables bind request fields from the path. `body` names the request field taken from the HTTP body, or `*` for every field the path did not bind; fields bound by neither become query parameters. `response_body` and `additional_bindings` refine it. A transcoding proxy or generated gateway reads the rule and converts JSON (via the proto3 JSON mapping) to the gRPC request and back. What the JSON face loses: HttpRule maps one request message to one response, so streaming methods depend on the transcoder; trailer metadata and the `google.rpc.Status` details must be flattened into headers, an HTTP status and a JSON body by the transcoder's own mapping.
code
http · 8 linesGET /v1/orders/8812 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{"name": "orders/8812", "status": "CANCELLED"}go deeper
Recall that one annotation on a gRPC method names an HTTP verb and URL, and a proxy translates JSON requests into the gRPC call.
Explain how request fields are split between the path template, the body mapping and query parameters, including what body "*" does.
Name what the JSON face loses - streaming, trailer metadata, rich status details - and decide whether transcoding or a separately designed HTTP API serves the audience.
Decide whether a two-faced service is a durable standard or a bridge, and who owns the error mapping and HTTP contract it creates.
## Why expose one service in two styles A team may build a service as gRPC for its internal callers - generated stubs, a `.proto` contract, HTTP/2 - and still need an HTTP/JSON face for browsers, partners or scripts that will not run a gRPC stack. **gRPC transcoding** gives both faces from one service implementation: the HTTP mapping is declared on each rpc, and a **transcoder** - a proxy in front of the service, or a gateway generated from the `.proto` - turns an HTTP/JSON request into the gRPC call and the gRPC response back into JSON. ## The HttpRule annotation The mapping is the `HttpRule` message in googleapis' `http.proto`, attached as the `google.api.http` option: ```protobuf service OrderService { rpc GetOrder(GetOrderRequest) returns (Order) { option (google.api.http) = { get: "/v1/{name=orders/*}" }; } rpc CancelOrder(CancelOrderRequest) returns (Order) { option (google.api.http) = { post: "/v1/{name=orders/*}:cancel" body: "*" }; } } message CancelOrderRequest { string name = 1; // bound by the path: orders/8812 string reason = 2; // body "*": taken from the JSON body } ``` Its fields, as `http.proto` defines them: | Field | Meaning | |---|---| | `get`, `put`, `post`, `delete`, `patch` | one of these (a `oneof`) holds the URL template for that HTTP method | | `custom` | a `CustomHttpPattern` with a `kind` (another method, such as `HEAD`, or `*`) and a `path` | | `body` | the request field mapped from the HTTP body, `*` for all fields not bound by the path, or omitted for no body | | `response_body` | a top-level response field to send as the HTTP body instead of the whole response message | | `additional_bindings` | further `HttpRule`s for the same method, nested only one level deep | | `selector` | which method the rule applies to, when rules live in service configuration rather than on the method | ## How request fields find their place - **Path template.** A template is segments separated by `/`, optionally ending in a `:` verb such as `:cancel`. A variable `{field}` binds a request field to one segment; `{name=orders/*}` binds it to a multi-segment pattern. `*` matches one segment and `**` zero or more, only at the end of the path (before any verb). Path variables must not refer to repeated or map fields. - **Body.** With `body: "message"`, that top-level field comes from the JSON body. With `body: "*"`, every field the path did not bind comes from the body and there are **no** query parameters. - **Query parameters.** With no body mapping, every field not in the path becomes a query parameter named by its field path (`?revision=2&sub.subfield=foo`); repeated message fields must not be mapped there. - **JSON.** The body is converted with the proto3 JSON mapping, so field names and value encodings follow that mapping. The `CancelOrder` binding above therefore accepts: ```http POST /v1/orders/8812:cancel HTTP/1.1 Content-Type: application/json {"reason": "customer request"} ``` The same `HttpRule` may instead be given in the service configuration's YAML; configured rules override matching annotations in the `.proto`. ## What the JSON face loses 1. **Streaming shapes.** An `HttpRule` maps a request message to a response message; nothing in it describes client-streaming, server-streaming or bidirectional calls. Whether a transcoder offers server streaming in some JSON form, or refuses streaming methods, is the transcoder's choice - an ordinary HTTP/1.1 request/response cannot carry a two-way message exchange. 2. **Trailer metadata.** gRPC always sends its status in HTTP/2 trailers, and custom metadata may travel in the response headers or the trailers. Which of it a transcoder copies into ordinary HTTP response headers is, again, its own mapping. 3. **Error fidelity.** A gRPC failure carries `grpc-status` (one of seventeen codes, 0 to 16), `grpc-message`, and optionally `grpc-status-details-bin` with a `google.rpc.Status` (`code`, `message`, `details` as typed `Any` messages). The JSON face must turn that into one HTTP status and a JSON body; the code-to-status mapping is defined by the transcoder, not by `HttpRule`, and several gRPC codes can land on the same HTTP status. 4. **Resource shape is not automatic.** Transcoding gives a REST-looking URL to whatever the method is. An RPC-shaped service yields `POST .../orders/8812:cancel` style endpoints; only a service designed around resources yields a natural REST face. ## When it is the right move Transcoding is worth it when one team owns the service, most callers want gRPC, and a smaller audience needs plain HTTP/JSON for unary operations. It is a poor fit when the HTTP audience is the main one, needs streaming, or relies on rich error details - then a separately designed HTTP API is clearer.
- In an HttpRule, what is the difference between body: "*" and omitting body?With `body: "*"`, every request field not bound by the path template is read from the HTTP body, so the endpoint takes no query parameters. With `body` omitted there is no HTTP request body at all: fields not in the path become URL query parameters. Naming a single field, such as `body: "order"`, takes just that top-level field from the body and leaves the rest to query parameters.
- How can one gRPC method answer on two different URLs?With `additional_bindings`: a list of further `HttpRule`s for the same method, each with its own verb and template - for example `/v1/messages/{message_id}` and `/v1/users/{user_id}/messages/{message_id}`. Nested bindings may not contain `additional_bindings` themselves, so the nesting is one level deep.
- Why can a path variable not bind a repeated field?`http.proto` states that path variables must not refer to repeated or map fields, because client libraries cannot expand such a variable into a URL path. A repeated primitive field can still travel as a repeated query parameter, `?tag=a&tag=b`.
saying these in an interview costs you the question
- Transcoding makes every streaming gRPC method available unchanged over plain HTTP/JSON.
- HttpRule defines the HTTP status code for each gRPC status code.
- With body: "*" the remaining fields still go to query parameters.
- Transcoding turns an RPC-shaped service into a resource-oriented REST API automatically.
- Path variables in an HttpRule template may bind any field, including repeated ones.