AIP-136
Custom operations
Services use custom operations to provide a means to express arbitrary actions that are difficult to model using only the standard operations. Custom operations are important because they provide a means for an API's vocabulary to adhere to user intent.
Guidance
Custom operations should only be used for functionality that can not be easily expressed via standard operations; prefer standard operations if possible, due to their consistent semantics. (Of course, this only applies if the functionality in question actually conforms to the normal semantics; it is not a good idea to contort things to endeavor to make the standard operations "sort of work".)
While custom operations vary widely in how they are designed, many principles apply consistently:
// Archives the given book.
rpc ArchiveBook(ArchiveBookRequest) returns (ArchiveBookResponse) {
option (google.api.http) = {
post: "/v1/{name=publishers/*/books/*}:archive"
body: "*"
};
}
- The name of the RPC should be a verb followed by a noun.
- The name must not contain prepositions ("for", "with", etc.).
- The HTTP method for custom operations should usually be
POST
, unless the custom method maps more strongly to another HTTP verb.- Custom operations that serve as an alternative to get or list operations
(such as
Search
) should useGET
. These operations must be idempotent and have no state changes or side effects (they should be safe as defined in RFC 7231). - Custom operations should not use
PATCH
orDELETE
.
- Custom operations that serve as an alternative to get or list operations
(such as
- The HTTP URI must use a
:
character followed by the custom verb (:archive
in the above example), and the verb in the URI must match the verb in the name of the RPC.- If word separation is required,
camelCase
must be used.
- If word separation is required,
- The
body
clause in thegoogle.api.http
annotation should be"*"
.- However, if using
GET
orDELETE
, thebody
clause must be absent.
- However, if using
- Custom operations should usually take a request message matching the RPC
name, with a -
Request
suffix. - Custom operations should usually return a response message matching the
RPC name, with a -
Response
suffix.- When operating on a specific resource, a custom method may return the resource itself.
/publishers/{publisherId}/books/{bookId}:archive:
post:
operationId: archiveBook
description: Archives the given book.
requestBody:
description:
content:
application/json:
schema:
description: Request structure to archive a book.
properties:
mime_type:
type: string
description: |
The target format for the archived book.
Must be "application/pdf", "application/rtf", or
"application/epub+zip"
required: true
enum:
- application/pdf
- application/rtf
- application/epub+zip
responses:
200:
description: OK
content:
application/json:
schema:
description: Response structure for the archiveBook operation.
properties:
uri:
type: string
description: The location of the archived book.
- The
operationId
should be a verb followed by a noun.- The
operationId
must not contain prepositions ("for", "with", etc.).
- The
- The HTTP method for custom operations should usually be
POST
, unless the custom method maps more strongly to another HTTP verb.- Custom operations that serve as an alternative to get or list operations
(such as
Search
) should useGET
, and require no request body. These operations must be idempotent and have no state changes or side effects (they should be safe as defined in RFC 7231). - Custom operations should not use
PATCH
orDELETE
.
- Custom operations that serve as an alternative to get or list operations
(such as
- The HTTP URI must use a
:
character followed by the custom verb (:archive
in the above example), and the verb in the URI must match the verb in theoperationId
.- If word separation is required,
camelCase
must be used.
- If word separation is required,
Note: The pattern above shows a custom method that operates on a specific resource. Custom operations can be associated with resources, collections, or services.
Collection-based custom operations
While most custom operations operate on a single resource, some custom operations may operate on a collection instead:
// Sorts the books from this publisher.
rpc SortBooks(SortBooksRequest) returns (SortBooksResponse) {
option (google.api.http) = {
post: "/v1/{publisher=publishers/*}/books:sort"
body: "*"
};
}
/publishers/{publisherId}/books:sort:
post:
operationId: sortBooks
description: Sorts the books from this publisher.
requestBody:
description: Request structure to sort a collection of books.
properties:
field:
type: string
description: |
The property of the book to sort by.
If not provided, "title" is used.
responses:
200:
description: OK
- If the collection has a parent, the field name in the request message
should be the target resource's singular noun (
publisher
in the above example). If word separators are necessary,snake_case
must be used. - The collection key (
books
in the above example) must be literal.
Stateless operations
Some custom operations are not attached to resources at all. These operations are generally stateless: they accept a request and return a response, and have no permanent effect on data within the API.
// Translates the provided text from one language to another.
rpc TranslateText(TranslateTextRequest) returns (TranslateTextResponse) {
option (google.api.http) = {
post: "/v1/{project=projects/*}:translateText"
body: "*"
};
}
/projects/{projectId}:translateText:
post:
operationId: translateText
description: Translates the provided text from one language to another.
requestBody:
description:
content:
application/json:
schema:
description: Request structure to translate text.
properties:
contents:
type: array
items:
type: string
description: The contents of the input, as a string.
source_language_code:
type: string
description: |
The BCP-47 language code of the input text (e.g. "en-US").
If the source language is not specified, the service will
attempt to infer it.
target_language_code:
type: string
description: |
The BCP-47 language code of the output text (e.g. "en-US").
responses:
200:
description: OK
content:
application/json:
schema:
description: |
Response structure for the translateText operation.
properties:
translated_text:
type: string
description: |
Text translated into the target language.
detected_language_code:
type: string
description: |
The BCP-47 language code of source text in the initial
request, if it was detected automatically.
- If the method runs in a particular scope (such as a project, as in the above
example), the field name in the request message should be the name of the
scope resource. If word separators are necessary,
snake_case
must be used. - The URI should place both the verb and noun after the
:
separator (avoid a "faux collection key" in the URI in this case, as there is no collection). For example,:translateText
is preferable totext:translate
. - Stateless operations must use
POST
if they involve billing.
Declarative-friendly resources
Declarative-friendly resources usually should not employ custom operations (except specific declarative-friendly custom operations discussed in other AIPs), because declarative-friendly tools are unable to automatically determine what to do with them.
An exception to this is for rarely-used, fundamentally imperative operations,
such as a Move
, Rename
, or Restart
operation, for which there would not
be an expectation of declarative support.