【Design】API Design

Posted by 西维蜀黍 on 2019-10-28, Last Modified on 2023-02-21

HTTP REST APIs

URL

Example

The following sections present a few real world examples on how to apply resource-oriented API design to large scale services. You can find more examples in the Google APIs repository.

In these examples, the asterisk indicates one specific resource out of the list.

Gmail API

The Gmail API service implements the Gmail API and exposes most of Gmail functionality. It has the following resource model:

  • API service: gmail.googleapis.com

    • A collection of users:users/*. Each user has the following resources.
      • A collection of messages: users/*/messages/*.
      • A collection of threads: users/*/threads/*.
      • A collection of labels: users/*/labels/*.
      • A collection of change history: users/*/history/*.
      • A resource representing the user profile: users/*/profile.
      • A resource representing user settings: users/*/settings.

Cloud Pub/Sub API

The pubsub.googleapis.com service implements the Cloud Pub/Sub API, which defines the following resource model:

  • API service:pubsub.googleapis.com

    • A collection of topics: projects/*/topics/*.
  • A collection of subscriptions: projects/*/subscriptions/*.

NOTE: Other implementations of the Pub/Sub API may choose different resource naming schemes.

Cloud Spanner API

The spanner.googleapis.com service implements the Cloud Spanner API, which defines the following resource model:

  • API service:spanner.googleapis.com
    • A collection of instances:projects/*/instances/*.
      • A collection of instance operations: projects/*/instances/*/operations/*.
      • A collection of databases: projects/*/instances/*/databases/*.
      • A collection of database operations: projects/*/instances/*/databases/*/operations/*.
      • A collection of database sessions: projects/*/instances/*/databases/*/sessions/*.

Methods

The following table describes how to map standard methods to HTTP methods:

Standard Method HTTP Mapping HTTP Request Body HTTP Response Body
List GET N/A Resource* list
Get GET N/A Resource*
Create POST Resource Resource*
Update PUT or PATCH Resource Resource*
Delete DELETE N/A google.protobuf.Empty**

*The resource returned from List, Get, Create, and Update methods may contain partial data if the methods support response field masks, which specify a subset of fields to be returned. In some cases, the API platform natively supports field masks for all methods.

List

The List method takes a collection name and zero or more parameters as input, and returns a list of resources that match the input.

List is commonly used to search for resources. List is suited to data from a single collection that is bounded in size and not cached. For broader cases, the custom method Search should be used.

A batch get (such as a method that takes multiple resource IDs and returns an object for each of those IDs) should be implemented as a custom BatchGet method, rather than a List. However, if you have an already-existing List method that provides the same functionality, you may reuse the List method for this purpose instead. If you are using a custom BatchGet method, it should be mapped to HTTP GET.

HTTP mapping:

  • The List method must use an HTTP GET verb.
  • The request message field(s) receiving the name of the collection whose resources are being listed should map to the URL path. If the collection name maps to the URL path, the last segment of the URL template (the collection ID) must be literal.
  • 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.
  • The response body should contain a list of resources along with optional metadata.

Get

The Get method takes a resource name, zero or more parameters, and returns the specified resource.

HTTP mapping:

  • The Get method must use an HTTP GET 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.
  • The returned resource shall map to the entire response body.

Create

Update

Delete

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. Standard Methods have simpler and well-defined semantics that most developers are familiar with, so they are easier to use and less error prone. Another advantage of standard methods is the API platform has better understanding and support for standard methods, such as billing, error handling, logging, monitoring.

A custom method can be associated with a resource, a collection, or a service. It may take an arbitrary request and return an arbitrary response, and also supports streaming request and response.

URL

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. For example, undelete a file can map to POST /files/a/long/file/name:undelete

Use Cases

Some additional scenarios where custom methods may be the right choice:

  • Reboot a virtual machine. The design alternatives could be “create a reboot resource in collection of reboots” which feels disproportionately complex, or “virtual machine has a mutable state which the client can update from RUNNING to RESTARTING” which would open questions as to which other state transitions are possible. Moreover, reboot is a well-known concept that can translate well to a custom method which intuitively meets developer expectations.
  • Send mail. Creating an email message should not necessarily send it (draft). Compared to the design alternative (move a message to an “Outbox” collection) custom method has the advantage of being more discoverable by the API user and models the concept more directly.
  • Promote an employee. If implemented as a standard update, the client would have to replicate the corporate policies governing the promotion process to ensure the promotion happens to the correct level, within the same career ladder etc.

Standard Fields

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.

Name Type Description
name string The name field should contain the relative resource name.
parent string For resource definitions and List/Create requests, the parent field should contain the parent relative resource name.
create_time Timestamp The creation timestamp of an entity.
update_time Timestamp The last update timestamp of an entity. Note: update_time is updated when create/patch/delete operation is performed.
delete_time Timestamp The deletion timestamp of an entity, only if it supports retention.
expire_time Timestamp The expiration timestamp of an entity if it happens to expire.
start_time Timestamp The timestamp marking the beginning of some time period.
end_time Timestamp The timestamp marking the end of some time period or operation (regardless of its success).
read_time Timestamp The timestamp at which a particular an entity should be read (if used in a request) or was read (if used in a response).
time_zone string The time zone name. It should be an IANA TZ name, such as “America/Los_Angeles”. For more information, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
region_code string The Unicode country/region code (CLDR) of a location, such as “US” and “419”. For more information, see http://www.unicode.org/reports/tr35/#unicode_region_subtag.
language_code string The BCP-47 language code, such as “en-US” or “sr-Latn”. For more information, see http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.
mime_type string An IANA published MIME type (also referred to as media type). For more information, see https://www.iana.org/assignments/media-types/media-types.xhtml.
display_name string The display name of an entity.
title string The official name of an entity, such as company name. It should be treated as the formal version of display_name.
description string One or more paragraphs of text description of an entity.
filter string The standard filter parameter for List methods.
query string The same as filter if being applied to a search method (ie :search)
page_token string The pagination token in the List request.
page_size int32 The pagination size in the List request.
total_size int32 The total count of items in the list irrespective of pagination.
next_page_token string The next pagination token in the List response. It should be used as page_token for the following request. An empty value means no more result.
order_by string Specifies the result ordering for List requests.
request_id string A unique string id used for detecting duplicated requests.
resume_token string An opaque token used for resuming a streaming request.
labels map Represents Cloud resource labels.
deleted bool If a resource allows undelete behavior, it must have a deleted field indicating the resource is deleted.
show_deleted bool If a resource allows undelete behavior, the corresponding List method must have a show_deleted field so client can discover the deleted resources.
update_mask FieldMask It is used for Update request message for performing partial update on a resource. This mask is relative to the resource, not to the request message.
validate_only bool If true, it indicates that the given request should only be validated, not executed.

Error

If you are a server developer, you should generate errors with enough information to help client developers understand and address the problem. At the same time, you must be aware of the security and privacy of the user data, and avoid disclosing sensitive information in the error message and error details, since errors are often logged and may be accessible by others. For example, an error message like “Client IP address is not on whitelist 128.0.0.0/8” exposes information about the server-side policy, which may not be accessible to the user.

Error Codes

Google APIs must use the canonical error codes defined by google.rpc.Code. Individual APIs should avoid defining additional error codes, since developers are very unlikely to write logic to handle a large number of error codes. 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.

Discussion

要不要保留 HTTP Method Code,比如 404、500,还是用错误码来表示异常情况(此时,所有 REST APIs 都返回 200 的 status code,而如果处理过程中出现异常,则返回500)。

个人是偏向于 HTTP Method Code,因为HTTP Method Code本身已经被标准化了,其语义更丰富,且通用性更好。

还有一个好处是,如果用response body的error code来表示异常情况,需要解析完整个response body。而如果假如这个body非常大,且执行这个操作与业务无关,比如在gateway端触发circuit breaker,这个解析操作的执行效率非常低(因为解析一个非常大的body,但只会使用到其中的error code)。

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.

Naming Convertions

Traditional RPC APIs

Methods

Only GET and POST method are allowed. We don’t use PUT and DELETE in project.

GET method is used to retrieve information from server, while POST requests are generally used to add / update / delete the data on server.

  • One notable exception is that, API that pass array to fetch batch data from server could be in POST method. This is because it is awkward to pass array of data to server using query string. You can pass array in JSON object in POST body. e.g.
# Simple request
POST /api/order/get_list

{"orders":[100001, 100002]}
# Complex request
POST /api/item/get_list

{"items":[{"shopid":1000,"itemid":100001},{"shopid":1000,"itemid":100002}]}

Reference