Common Design Patterns

Empty Responses

The standard Delete method must return google.protobuf.Empty for global consistency. It also prevents clients from depending on additional metadata that would not be available during retry. For custom methods, they must have their own XxxResponse messages even if they are empty, because it is very likely their functionality will grow over time and need to return additional data.

Representing Ranges

Fields that represent ranges should use half-open intervals with naming convention [start_xxx, end_xxx), such as [start_key, end_key) or [start_time, end_time). Half-open interval semantics is commonly used by C++ STL library and Java standard library. APIs should avoid using other ways of representing ranges, such as (index, count), or [first, last].

Resource Labels

In a resource-oriented API, the resource schema is defined by the API. To let the client attach small amount of simple metadata to the resources (for example, tagging a virtual machine resource as a database server), APIs should use the resource labels design pattern described in google.api.LabelDescriptor.

To do so, the API design should add a field map<string, string> labels to the resource definition.

message Book {
  string name = 1;
  map<string, string> labels = 2;
}

Long Running Operations

If an API method typically takes a long time to complete, it can be designed to return a Long Running Operation resource to the client, which the client can use to track the progress and receive the result. The Operation defines a standard interface to work with long running operations. Individual APIs must not define their own interfaces for long running operations to avoid inconsistency.

The operation resource must be returned directly as the response message and any immediate consequence of the operation should be reflected in the API. For example, when creating a resource, that resource should appear in LIST and GET methods though the resource should indicate that it is not ready for use. When the operation is complete, the Operation.response field should contain the message that would have been returned directly, if the method was not long running.

List Pagination

Listable collections should support pagination, even if results are typically small.

Rationale: Even though adding pagination support to an existing API is purely additive from API surface perspective, it is a behavior-breaking change. Existing clients unaware of pagination will incorrectly assume that they received complete list result where they only receive the first page instead.

To support pagination (returning list results in pages) in a List method, the API shall:

  • define a string field page_token in the List method's request message. The client uses this field to request a specific page of the list results.
  • define an int32 field page_size in the List method's request message. Clients use this field to specify the maximum number of results to be returned by the server. The server may further constrain the maximum number of results returned in a single page. If the page_size is 0, the server will decide the number of results to be returned.
  • define a string field next_page_token in the List method's response message. This field represents the pagination token to retrieve the next page of results. If the value is "", it means no further results for the request.

To retrieve the next page of results, client shall pass the value of response's next_page_token in the subsequent List method call (in the request message's page_token field):

rpc ListBooks(ListBooksRequest) returns (ListBooksResponse);

message ListBooksRequest {
  string name = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ListBooksResponse {
  repeated Book books = 1;
  string next_page_token = 2;
}

When clients pass in query parameters in addition to a page token, the service must fail the request if the query parameters are not consistent with the page token.

Page token contents should be a web-safe base64 encoded protocol buffer. This allows the contents to evolve without compatibility issues. If the page token contains potentially sensitive information, that information should be encrypted. Services must prevent tampering with page tokens from exposing unintended data through one of the following methods:

  • require query parameters to be respecifed on follow up requests.
  • only reference server-side session state in the page token.
  • encrypt and sign the query parameters in the page token and revalidate and reauthorize these parameters on every call.

An implementation of pagination may also provide the total count of items in an int32 field named total_size.

List Sub-Collections

Sometimes, an API needs to let a client List/Search across sub- collections. For example, the Library API has a collection of shelves, and each shelf has a collection of books, and a client wants to search for a book across all shelves. In such cases, it is recommended to use standard List on the sub-collection and specify the wildcard collection id "-" for the parent collection(s). For the Library API example, we can use the following REST API request:

GET https://library.googleapis.com/v1/shelves/-/books?filter=xxx

NOTE: the reason to choose "-" instead of "*" is to avoid the need for URL escaping.

Get Unique Resource From Sub-Collection

Sometimes, a resource within a sub-collection has an identifier that is unique within its parent collection(s). In this case, it may be useful to allow a Get to retrieve that resource without knowing which parent collection contains it. In such cases, it is recommended to use a standard Get on the resource and specify the wildcard collection id "-" for all parent collections within which the resource is unique. For example, in the Library API, we can use the following REST API request, if the book is unique among all books on all shelves:

GET https://library.googleapis.com/v1/shelves/-/books/{id}

The resource name in the response to this call must use the canonical name of the resource, with actual parent collection identifiers instead of "-" for each parent collection. For example, the request above should return a resource with a name like shelves/shelf713/books/book8141, not shelves/-/books/book8141.

Sorting Order

If an API method lets client specify sorting order for list results, the request message should contain a field:

string order_by = ...;

The string value should follow SQL syntax: comma separated list of fields. For example: "foo,bar". The default sorting order is ascending. To specify descending order for a field, a suffix " desc" should be appended to the field name. For example: "foo desc,bar".

Redundant space characters in the syntax are insignificant. "foo,bar desc" and "  foo ,  bar  desc  " are equivalent.

Request Validation

If an API method has side effects and there is a need to validate the request without causing such side effects, the request message should contain a field:

bool validate_only = ...;

If this field is set to true, the server must not execute any side effects and only perform implementation-specific validation consistent with the full request.

If validation succeeds, google.rpc.Code.OK must be returned and any full request using the same request message should not return google.rpc.Code.INVALID_ARGUMENT. Note that the request may still fail due to other errors such as google.rpc.Code.ALREADY_EXISTS or because of race conditions.

Request Duplication

For network APIs, idempotent API methods are highly preferred, because they can be safely retried after network failures. However, some API methods cannot easily be idempotent, such as creating a resource, and there is a need to avoid unnecessary duplication. For such use cases, the request message should contain a unique ID, like a UUID, which the server will use to detect duplication and make sure the request is only processed once.

// A unique request ID for server to detect duplicated requests.
// This field **should** be named as `request_id`.
string request_id = ...;

If a duplicate request is detected, the server should return the response for the previously successful request, because the client most likely did not receive the previous response.

Enum Default Value

Every enum definition must start with a 0 valued entry, which shall be used when an enum value is not explicitly specified. APIs must document how 0 values are handled.

If there is a common default behavior, then the enum value 0 should be used, and the API should document the expected behavior.

If there is no common default behavior, then the enum value 0 should be named as ENUM_TYPE_UNSPECIFIED and should be rejected with error INVALID_ARGUMENT when used.

enum Isolation {
  // Not specified.
  ISOLATION_UNSPECIFIED = 0;
  // Reads from a snapshot. Collisions occur if all reads and writes cannot be
  // logically serialized with concurrent transactions.
  SERIALIZABLE = 1;
  // Reads from a snapshot. Collisions occur if concurrent transactions write
  // to the same rows.
  SNAPSHOT = 2;
  ...
}

// When unspecified, the server will use an isolation level of SNAPSHOT or
// better.
Isolation level = 1;

An idiomatic name may be used for the 0 value. For example, google.rpc.Code.OK is the idiomatic way of specifying the absence of an error code. In this case, OK is semantically equivalent to UNSPECIFIED in the context of the enum type.

In cases where an intrinsically sensible and safe default exists, that value may be used for the '0' value. For example, BASIC is the '0' value in the Resource View enum.

Grammar Syntax

In some API designs, it is necessary to define simple grammars for certain data formats, such as acceptable text input. To provide a consistent developer experience across APIs and reduce learning curve, API designers must use the ISO 14977 Extended Backus-Naur Form (EBNF) syntax to define such grammars:

Production  = name "=" [ Expression ] ";" ;
Expression  = Alternative { "|" Alternative } ;
Alternative = Term { Term } ;
Term        = name | TOKEN | Group | Option | Repetition ;
Group       = "(" Expression ")" ;
Option      = "[" Expression "]" ;
Repetition  = "{" Expression "}" ;

NOTE: TOKEN represents terminals defined outside the grammar.

Integer Types

In API designs, unsigned integer types such as uint32 and fixed32 should not be used because some important programming languages and systems don't support them well, such as Java, JavaScript and OpenAPI. And they are more likely to cause overflow errors. Another issue is that different APIs are very likely to use mismatched signed and unsigned types for the same thing.

When signed integer types are used for things where the negative values are not meaningful, such as size or timeout, the value -1 (and only -1) may be used to indicate special meaning, such as end of file (EOF), infinite timeout, unlimited quota limit, or unknown age. Such usages must be clearly documented to avoid confusion. API producers should also document the behavior of the implicit default value 0 if it is not very obvious.

Partial Response

Sometimes an API client only needs a specific subset of data in the response message. To support such use cases, some API platforms provide native support for partial responses. Google API Platform supports it through response field mask. For any REST API call, there is an implicit system query parameter $fields, which is the JSON representation of a google.protobuf.FieldMask value. The response message will be filtered by the $fields before being sent back to the client. This logic is handled automatically for all API methods by the API Platform.

GET https://library.googleapis.com/v1/shelves?$fields=name

Resource View

To reduce network traffic, it is sometimes useful to allow the client to limit which parts of the resource the server should return in its responses, returning a view of the resource instead of the full resource representation. The resource view support in an API is implemented by adding a parameter to the method request which allows the client to specify which view of the resource it wants to receive in the response.

The parameter:

  • should be of an enum type
  • must be named view

Each value of the enumeration defines which parts of the resource (which fields) will be returned in the server's response. Exactly what is returned for each view value is implementation-defined and should be specified in the API documentation.

package google.example.library.v1;

service Library {
  rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
    option (google.api.http) = {
      get: "/v1/{name=shelves/*}/books"
    }
  };
}

enum BookView {
  // Server responses only include author, title, ISBN and unique book ID.
  // The default value.
  BASIC = 0;

  // Full representation of the book is returned in server responses,
  // including contents of the book.
  FULL = 1;
}

message ListBooksRequest {
  string name = 1;

  // Specifies which parts of the book resource should be returned
  // in the response.
  BookView view = 2;
}

This construct will be mapped to URLs such as:

GET https://library.googleapis.com/v1/shelves/shelf1/books?view=BASIC

You can find out more about defining methods, requests, and responses in the Standard Methods chapter of this Design Guide.

ETags

An ETag is an opaque identifier allowing a client to make conditional requests. To support ETags, an API should include a string field etag in the resource definition, and its semantics must match the common usage of ETag. Normally, etag contains the fingerprint of the resource computed by the server. See Wikipedia and RFC 7232 for more details.

ETags can be either strongly or weakly validated, where weakly validated ETags are prefixed with W/. In this context, strong validation means that two resources bearing the same ETag have both byte-for-byte identical content and identical extra fields (ie, Content-Type). This means that strongly validated ETags permit for caching of partial responses to be assembled later.

Conversely, resources bearing the same weakly validated ETag value means that the representations are semantically equivalent, but not necessarily byte-for-byte identical, and therefore not suitable for response caching of byte-range requests.

For example:

// This is a strong ETag, including the quotes.
"1a2f3e4d5b6c7c"
// This is a weak ETag, including the prefix and quotes.
W/"1a2b3c4d5ef"

Output Fields

APIs may want to distinguish between fields that are provided by the client as inputs and fields that are only returned by the server on output on a particular resource. For fields that are output only, the field attribute shall be documented.

Note that if output only fields are set in the request by the client or the client specifies a google.protobuf.FieldMask with output only fields, the server must accept the request without error. This means that the server must ignore the presence of output only fields and any indication of it. The reason for this recommendation is because clients will commonly reuse resources returned by the server as another request input, e.g. a retrieved Book will be later reused in an UPDATE method. If output only fields are validated against, then this places extra work on the client to clear out output only fields.

message Book {
  string name = 1;
  // Output only.
  Timestamp create_time = 2;
}

Send feedback about...