|
1 | 1 | # HTTP and gRPC Transcoding |
2 | 2 |
|
3 | | -**Note:** This AEP has not yet been adopted. See |
4 | | -[this GitHub issue](https://github.com/aep-dev/aep.dev/issues/51) for more |
5 | | -information. |
| 3 | +APIs that follow [resource-oriented design][resources] are defined using |
| 4 | +[RPCs][rpc], but the resource-oriented design framework allows them to also be |
| 5 | +presented as APIs that largely follow REST/JSON conventions. This is important |
| 6 | +in order to help developers use their existing knowledge: over 80% of the |
| 7 | +public APIs available follow most REST conventions, and developers are |
| 8 | +accustomed to that pattern. |
| 9 | + |
| 10 | +## Guidance |
| 11 | + |
| 12 | +Protobuf APIs **must** provide HTTP definitions for each RPC that they define, except |
| 13 | +for bi-directional streaming RPCs, which can not be natively supported using |
| 14 | +HTTP/1.1. When providing a bi-directional streaming method, an API **should** |
| 15 | +also offer an alternative method that does not rely on bi-directional |
| 16 | +streaming. |
| 17 | + |
| 18 | +### HTTP method and path |
| 19 | + |
| 20 | +When using protocol buffers, each RPC **must** define the HTTP method and path |
| 21 | +using the [`google.api.http`][google.api.http] annotation: |
| 22 | + |
| 23 | +```proto |
| 24 | +rpc CreateBook(CreateBookRequest) returns (Book) { |
| 25 | + option (google.api.http) = { |
| 26 | + post: "/v1/{parent=publishers/*}/books" |
| 27 | + body: "book" |
| 28 | + }; |
| 29 | +} |
| 30 | + |
| 31 | +message CreateBookRequest { |
| 32 | + // The publisher who will publish this book. |
| 33 | + // When using HTTP/JSON, this field is automatically populated based |
| 34 | + // on the URI, because of the `{parent=publishers/*}` syntax. |
| 35 | + string parent = 1 [ |
| 36 | + (google.api.field_behavior) = REQUIRED, |
| 37 | + (google.api.resource_reference) = { |
| 38 | + child_type: "library.example.com/Book" |
| 39 | + }]; |
| 40 | + |
| 41 | + // The book to create. |
| 42 | + // When using HTTP/JSON, this field is populated based on the HTTP body, |
| 43 | + // because of the `body: "book"` syntax. |
| 44 | + Book book = 2 [(google.api.field_behavior) = REQUIRED]; |
| 45 | + |
| 46 | + // The user-specified ID for the book. |
| 47 | + // When using HTTP/JSON, this field is populated based on a query string |
| 48 | + // argument, such as `?bookId=foo`. This is the fallback for fields that |
| 49 | + // are not included in either the URI or the body. |
| 50 | + // Note that clients use camelCase format to communicate the field names |
| 51 | + // to the service. |
| 52 | + string book_id = 3; |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +- The first key (`post` in this example) corresponds to the HTTP method. RPCs |
| 57 | + **may** use `get`, `post`, `patch`, or `delete`. |
| 58 | + - RPCs **must** use the prescribed HTTP verb for each standard method, as |
| 59 | + discussed in [Get](/get), [List](/list), [Create](/create), [Update](/update), and |
| 60 | + [Delete](/delete) |
| 61 | + - RPCs **should** use the prescribed HTTP verb for custom methods, as |
| 62 | + discussed in [Custom Methods](/custom-methods). |
| 63 | + - RPCs **should not** use `put` or `custom`. |
| 64 | +- The corresponding value represents the URI. |
| 65 | + - URIs **must** use the `{foo=bar/*}` syntax to represent a variable that |
| 66 | + should be populated in the request proto. When extracting a [resource |
| 67 | + name](/resource-paths), the variable **must** include the entire resource name, not |
| 68 | + just the ID component. |
| 69 | + - URIs **may** use nested fields for their variable names. (Additionally, |
| 70 | + AEP-134 mandates this for `Update` requests.) |
| 71 | + - URIs **must** use the `*` character to represent ID components, which |
| 72 | + matches all URI-safe characters except for `/`. URIs **may** use `**` as |
| 73 | + the final segment of a URI if matching `/` is required. |
| 74 | +- The `body` key defines which single top-level field in the request will be |
| 75 | + sent as the HTTP body. If the body is `*`, then this indicates that the |
| 76 | + request object itself is the HTTP body. The request body is encoded as JSON |
| 77 | + as defined by protocol buffers' canonical [JSON encoding][json encoding]. |
| 78 | + - RPCs **must not** define a `body` at all for RPCs that use the `GET` or |
| 79 | + `DELETE` HTTP verbs. |
| 80 | + - RPCs **must** use the prescribed `body` for Create ([Create](/create)) and Update |
| 81 | + ([AEP-134](/update)) requests. |
| 82 | + - RPCs **should** use the prescribed `body` for custom methods ([Custom Methods](custom-methods)). |
| 83 | + - The `body` **must not** contain a nested field (or use the `.` character), |
| 84 | + - The `body` **must not** be the same as a URI parameter. |
| 85 | + - The `body` **must not** be a `repeated` field. |
| 86 | + - Fields **should not** use the `json_name` annotation to alter the field |
| 87 | + name in JSON, unless doing so for backwards-compatibility reasons. |
| 88 | + |
| 89 | +**Note:** Bi-directional streaming RPCs should not include a `google.api.http` |
| 90 | +annotation at all. If feasible, the service **should** provide non-streaming |
| 91 | +equivalent RPCs. |
| 92 | + |
| 93 | +### Multiple URI bindings |
| 94 | + |
| 95 | +Occasionally, an RPC needs to correspond to more than one URI: |
| 96 | + |
| 97 | +```proto |
| 98 | +rpc CreateBook(CreateBookRequest) returns (Book) { |
| 99 | + option (google.api.http) = { |
| 100 | + post: "/v1/{parent=publishers/*}/books" |
| 101 | + body: "book" |
| 102 | + additional_bindings: { |
| 103 | + post: "/v1/{parent=authors/*}/books" |
| 104 | + body: "book" |
| 105 | + } |
| 106 | + additional_bindings: { |
| 107 | + post: "/v1/books" |
| 108 | + body: "book" |
| 109 | + } |
| 110 | + }; |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +- RPCs **may** define any number of additional bindings. The structure is |
| 115 | + identical to the `google.api.http` annotation (in fact, it is a recursive |
| 116 | + reference). |
| 117 | +- RPCs **must not** define an additional binding within an additional binding. |
| 118 | +- The `body` clause **must** be identical in the top-level annotation and each |
| 119 | + additional binding. |
| 120 | + |
| 121 | +<!-- prettier-ignore-start --> |
| 122 | +[json encoding]: https://developers.google.com/protocol-buffers/docs/proto3#json |
| 123 | +[rpc]: https://en.wikipedia.org/wiki/Remote_procedure_call |
| 124 | +[google.api.http]: https://buf.build/googleapis/googleapis/docs/main:google.api#google.api.Http |
| 125 | +<!-- prettier-ignore-end --> |
0 commit comments