Skip to content

Instantly share code, notes, and snippets.

@farhadkzm
Last active September 15, 2017 02:14
Show Gist options
  • Save farhadkzm/fefffe35c7e4fd224cd8aeac759b61a7 to your computer and use it in GitHub Desktop.
Save farhadkzm/fefffe35c7e4fd224cd8aeac759b61a7 to your computer and use it in GitHub Desktop.
Notes on designing of a REST API. Mostly from Google's Design Guide for REST APIs
  • The core principle is to define named resources that can be manipulated using a small number of methods.
  • resources and methods are nouns and verbs of the API
  • resource names map to URLs and methods map to HTTP methods.
@farhadkzm
Copy link
Author

farhadkzm commented May 29, 2017

Update - HTTP PATCH, or sometimes PUT
The Update method takes a request message containing a resource and zero or more parameters. It updates the specified resource and its properties, and returns the updated resource.
Mutable resource properties should be mutable by the Update method, except the properties that contain the resource's name or parent. Any functionality to rename or move a resource must not happen in the Update method and instead shall be handled by a custom method.

  • The standard Update method should support partial resource update, and use HTTP verb PATCH with a FieldMask field named update_mask
  • An Update method that requires more advanced patching semantics, such as appending to a repeated field, should be made available by a custom method.
  • If the Update method only supports full resource update, it must use HTTP verb PUT. However, full update is highly discouraged because it has backwards compatibility issues when adding new resource fields. (TODO: How is this an issue?!)
  • The message field receiving the resource name must map to the URL path. The field may be in the resource message itself.
  • The request message field containing the resource must map to the request body.
  • All remaining request message fields must map to the URL query parameters.
  • The response message must be the updated resource itself.

@farhadkzm
Copy link
Author

farhadkzm commented May 29, 2017

Delete

  • The Delete method must use an HTTP DELETE verb.
  • The request message field(s) receiving the resource name should map to the URL path.
  • All remaining request message fields shall map to the URL query parameters.
  • There is no request body; the API configuration must not declare a body clause.
  • If the Delete method
    • immediately removes: it should return empty.
    • initiates a long-running operation, it should return the long-running operation.
    • only marks the resource as being deleted, it should return the updated resource.

Delete is idempotent in effect but subsequent calls should result in a NOT_FOUND

@farhadkzm
Copy link
Author

farhadkzm commented May 29, 2017

idempotent methods
Aside from error or expiration issues, the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property. Also, the methods OPTIONS and TRACE SHOULD NOT have side effects, and so are inherently idempotent.

Note that idempotent calls can have different response.
https://tools.ietf.org/html/rfc2616#section-9.1.2

@farhadkzm
Copy link
Author

farhadkzm commented May 31, 2017

Custom methods
Custom methods refer to API methods besides the 5 standard methods. They should only be used for functionality that cannot be easily expressed via standard methods. In general, API designers should choose standard methods over custom methods whenever feasible.

A custom method can be associated with a resource, a collection, or a service. For custom methods, they should use the following generic HTTP mapping:
https://service.name/v1/some/resource/name:customVerb
The reason to use : instead of / to separate the custom verb from the resource name is to support arbitrary paths.
Notes:

  • Custom methods should use HTTP POST verb since it has the most flexible semantics.
  • Notably, custom methods using HTTP GET must be idempotent and have no side effects.

Custom method has the advantage of being more discoverable by the API user and models the concept more directly.

@farhadkzm
Copy link
Author

This section describes a set of standard message field definitions that should be used when similar concepts are needed. This will ensure the same concept has the same name and semantics across different APIs.
https://cloud.google.com/apis/design/standard_fields

@farhadkzm
Copy link
Author

Errors*

Error Messages

The error message should help users understand and resolve the API error easily and quickly. In general, consider the following guidelines when writing error messages:

  • Do not assume the user is an expert user of your API. Users could be client developers, operations people, IT staff, or end-users of apps.
  • Do not assume the user knows anything about your service implementation or is familiar with the context of the errors (such as log analysis).
  • When possible, error messages should be constructed such that a technical user (but not necessarily a developer of your API) can respond to the error and correct it.
  • Keep the error message brief. If needed, provide a link where a confused reader can ask questions, give feedback, or get more information that doesn't cleanly fit in an error message. Otherwise, use the details field to expand.

@farhadkzm
Copy link
Author

For reference, handling an average of 3 error codes per API call would mean most application logic would just be for error handling, which would not be a good developer experience.

@farhadkzm
Copy link
Author

Error Localization

The message field in google.rpc.Status is developer-facing and must be in English.

If a user-facing error message is needed, use google.rpc.LocalizedMessage as your details field. While the message field in google.rpc.LocalizedMessage can be localized, ensure that the message field in google.rpc.Status is in English.

By default, the API service should use the authenticated user’s locale or HTTP Accept-Language header to determine the language for the localization.

@farhadkzm
Copy link
Author

Common Design Patterns

Long Running Operations
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. This is because 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.

@farhadkzm
Copy link
Author

List Sub-Collections

Sometimes, an API needs to let a client List/Search across sub- collections.
For the Library API example, we can use the following REST API request:

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

@farhadkzm
Copy link
Author

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.

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

@farhadkzm
Copy link
Author

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"

@farhadkzm
Copy link
Author

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.

@farhadkzm
Copy link
Author

farhadkzm commented Jun 2, 2017

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.

@farhadkzm
Copy link
Author

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.

@farhadkzm
Copy link
Author

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.

@farhadkzm
Copy link
Author

Partial Response
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.

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

@farhadkzm
Copy link
Author

farhadkzm commented Jun 7, 2017

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

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

@farhadkzm
Copy link
Author

ETag
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.
ETags can be either strongly or weakly validated, where weakly validated ETags are prefixed with W/.

@farhadkzm
Copy link
Author

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;
}```

@farhadkzm
Copy link
Author

farhadkzm commented Jun 8, 2017

Versioning

A new major version of an API must not depend on a previous major version of the same API. An API may depend on other APIs with the understanding of dependency and stability risk associated with those APIs. A stable API version must only depend on the latest stable version of other APIs.

For some period of time, different versions of the same API must be able to work at the same time within a single client application. This is to help the client smoothly transition from the older version to the newer version of the API.

An older API version should only be removed after its deprecation period is over.

Common and stable data types that are shared by many APIs, such as date and time, should be defined in a separate proto package. If a breaking change ever becomes necessary, either a new type name, or a package name with a new major version must be introduced.

Compatibility
To put it another way: old clients should be able to work against newer servers within the same major version number, and when they want to update to a new minor version (for example to take advantage of a new feature) they should be able to do so easily.

@farhadkzm
Copy link
Author

farhadkzm commented Jun 9, 2017

URI and URL
Further according to the contemporary view, the term "URL" does not refer to a formal partition of URI space; rather, URL is a useful but informal concept: a URL is a type of URI that identifies a resource via a representation of its primary access mechanism (e.g., its network "location"), rather than by some other attributes it may have. Thus as we noted, "http:" is a URI scheme. An http URI is a URL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment