Skip to content

Latest commit

 

History

History
369 lines (277 loc) · 14.1 KB

urls.adoc

File metadata and controls

369 lines (277 loc) · 14.1 KB

REST Basics - URLs

Guidelines for naming and designing resource paths and query parameters.

{SHOULD} not use /api as base path

In most cases, all resources provided by a service are part of the public API, and therefore should be made available under the root "/" base path.

If the service should also support non-public, internal APIs — for specific operational support functions, for example — we encourage you to maintain two different API specifications and provide API audience. For both APIs, you should not use /api as base path.

We see API’s base path as a part of deployment variant configuration. Therefore, this information has to be declared in the server object.

{MUST} pluralize resource names

Usually, a collection of resource instances is provided (at least the API should be ready here). The special case of a resource singleton must be modeled as a collection with cardinality 1 including definition of maxItems = minItems = 1 for the returned array structure to make the cardinality constraint explicit.

Exception: the pseudo identifier self used to specify a resource endpoint where the resource identifier is provided by authorization information (see {MUST} identify resources and sub-resources via path segments).

{MUST} use URL-friendly resource identifiers

To simplify encoding of resource IDs in URLs they must match the regex [a-zA-Z0-9:._\-/]*. Resource IDs only consist of ASCII strings using letters, numbers, underscore, minus, colon, period, and - on rare occasions - slash.

Note: slashes are only allowed to build and signal resource identifiers consisting of compound keys.

Note: to prevent ambiguities of unnormalized paths resource identifiers must never be empty. Consequently, support of empty strings for path parameters is forbidden.

{MUST} use kebab-case for path segments

Path segments are restricted to ASCII kebab-case strings matching regex ^[a-z][a-z\-0-9]*$. The first character must be a lower case letter, and subsequent characters can be a letter, or a dash(-), or a number.

Example:

/shipment-orders/{shipment-order-id}

Hint: kebab-case applies to concrete path segments and not necessarily the names of path parameters.

{MUST} use normalized paths without empty path segments and trailing slashes

You must not specify paths with duplicate or trailing slashes, e.g. /customers//addresses or /customers/. As a consequence, you must also not specify or use path variables with empty string values.

Note: Non standard paths have no clear semantics. As a result, behavior for non standard paths varies between different HTTP infrastructure components and libraries. This may leads to ambiguous and unexpected results during request handling and monitoring.

We recommend to implement services robust against clients not following this rule. All services should normalize request paths before processing by removing duplicate and trailing slashes. Hence, the following requests should refer to the same resource:

GET /orders/{order-id}
GET /orders/{order-id}/
GET /orders//{order-id}

Note: path normalization is not supported by all framework out-of-the-box. Services are required to support at least the normalized path while rejecting all alternatives paths, if failing to deliver the same resource.

{MUST} keep URLs verb-free

The API describes resources, so the only place where actions should appear is in the HTTP methods. In URLs, use only nouns. Instead of thinking of actions (verbs), it’s often helpful to think about putting a message in a letter box: e.g., instead of having the verb cancel in the url, think of sending a message to cancel an order to the cancellations letter box on the server side.

{MUST} avoid actions — think about resources

REST is all about your resources, so consider the domain entities that take part in web service interaction, and aim to model your API around these using the standard HTTP methods as operation indicators. For instance, if an application has to lock articles explicitly so that only one user may edit them, create an article lock with {PUT} or {POST} instead of using a lock action.

Request:

PUT /article-locks/{article-id}

The added benefit is that you already have a service for browsing and filtering article locks.

{SHOULD} define useful resources

As a rule of thumb resources should be defined to cover 90% of all its client’s use cases. A useful resource should contain as much information as necessary, but as little as possible. A great way to support the last 10% is to allow clients to specify their needs for more/less information by supporting filtering and embedding.

{MUST} use domain-specific resource names

API resources represent elements of the application’s domain model. Using domain-specific nomenclature for resource names helps developers to understand the functionality and basic semantics of your resources. It also reduces the need for further documentation outside the API definition. For example, "sales-order-items" is superior to "order-items" in that it clearly indicates which business object it represents. Along these lines, "items" is too general.

{SHOULD} model complete business processes

An API should contain the complete business processes containing all resources representing the process. This enables clients to understand the business process, foster a consistent design of the business process, allow for synergies from description and implementation perspective, and eliminates implicit invisible dependencies between APIs.

In addition, it prevents services from being designed as thin wrappers around databases, which normally tends to shift business logic to the clients.

{MUST} identify resources and sub-resources via path segments

Some API resources may contain or reference sub-resources. Embedded sub-resources, which are not top-level resources, are parts of a higher-level resource and cannot be used outside of its scope. Sub-resources should be referenced by their name and identifier in the path segments as follows:

/resources/{resource-id}/sub-resources/{sub-resource-id}

In order to improve the consumer experience, you should aim for intuitively understandable URLs, where each sub-path is a valid reference to a resource or a set of resources. For instance, if /partners/{partner-id}/addresses/{address-id} is valid, then, in principle, also /partners/{partner-id}/addresses, /partners/{partner-id} and /partners must be valid. Examples of concrete url paths:

/shopping-carts/de:1681e6b88ec1/items/1
/shopping-carts/de:1681e6b88ec1
/content/images/9cacb4d8
/content/images

Note: resource identifiers may be build of multiple other resource identifiers (see {MAY} expose compound keys as resource identifiers).

Exception: In some situations the resource identifier is not passed as a path segment but via the authorization information, e.g. an authorization token or session cookie. Here, it is reasonable to use self as pseudo-identifier path segment. For instance, you may define /employees/self or /employees/self/personal-details as resource paths —  and may additionally define endpoints that support identifier passing in the resource path, like define /employees/{empl-id} or /employees/{empl-id}/personal-details.

{MAY} expose compound keys as resource identifiers

If a resource is best identified by a compound key consisting of multiple other resource identifiers, it is allowed to reuse the compound key in its natural form containing slashes instead of technical resource identifier in the resource path without violating the above rule {MUST} identify resources and sub-resources via path segments as follows:

/resources/{compound-key-1}[delim-1]...[delim-n-1]{compound-key-n}

Example paths:

/shopping-carts/{country}/{session-id}
/shopping-carts/{country}/{session-id}/items/{item-id}
/api-specifications/{docker-image-id}/apis/{path}/{file-name}
/api-specifications/{repository-name}/{artifact-name}:{tag}
/article-size-advices/{sku}/{sales-channel}

Note: Exposing a compound key as described above limits ability to evolve the structure of the resource identifier as it is no longer opaque.

To compensate for this drawback, APIs must apply a compound key abstraction consistently in all requests and responses parameters and attributes allowing consumers to treat these as technical resource identifier replacement. The use of independent compound key components must be limited to search and creation requests, as follows:

# compound key components passed as independent search query parameters
GET /article-size-advices?skus=sku-1,sku-2&sales_channel_id=sid-1
=> { "items": [{ "id": "id-1", ...  },{ "id": "id-2", ...  }] }

# opaque technical resource identifier passed as path parameter
GET /article-size-advices/id-1
=> { "id": "id-1", "sku": "sku-1", "sales_channel_id": "sid-1", "size": ... }

# compound key components passed as mandatory request fields
POST /article-size-advices { "sku": "sku-1", "sales_channel_id": "sid-1", "size": ... }
=> { "id": "id-1", "sku": "sku-1", "sales_channel_id": "sid-1", "size": ... }

Where id-1 is representing the opaque provision of the compound key sku-1/sid-1 as technical resource identifier.

Remark: A compound key component may itself be used as another resource identifier providing another resource endpoint, e.g /article-size-advices/{sku}.

{MAY} consider using (non-) nested URLs

If a sub-resource is only accessible via its parent resource and may not exist without parent resource, consider using a nested URL structure, for instance:

/shoping-carts/de/1681e6b88ec1/cart-items/1

However, if the resource can be accessed directly via its unique id, then the API should expose it as a top-level resource. For example, customer has a collection for sales orders; however, sales orders have globally unique id and some services may choose to access the orders directly, for instance:

/customers/1637asikzec1
/sales-orders/5273gh3k525a

{SHOULD} limit number of resource types

To keep maintenance and service evolution manageable, we should follow "functional segmentation" and "separation of concern" design principles and do not mix different business functionalities in same API definition. In practice this means that the number of resource types exposed via an API should be limited. In this context a resource type is defined as a set of highly related resources such as a collection, its members and any direct sub-resources.

For example, the resources below would be counted as three resource types, one for customers, one for the addresses, and one for the customers' related addresses:

/customers
/customers/{id}
/customers/{id}/preferences
/customers/{id}/addresses
/customers/{id}/addresses/{addr}
/addresses
/addresses/{addr}

Note that:

  • We consider /customers/{id}/preferences part of the /customers resource type because it has a one-to-one relation to the customer without an additional identifier.

  • We consider /customers and /customers/{id}/addresses as separate resource types because /customers/{id}/addresses/{addr} also exists with an additional identifier for the address.

  • We consider /addresses and /customers/{id}/addresses as separate resource types because there’s no reliable way to be sure they are the same.

Given this definition, our experience is that well defined APIs involve no more than 4 to 8 resource types. There may be exceptions with more complex business domains that require more resources, but you should first check if you can split them into separate subdomains with distinct APIs.

Nevertheless one API should hold all necessary resources to model complete business processes helping clients to understand these flows.

{SHOULD} limit number of sub-resource levels

There are main resources (with root url paths) and sub-resources (or nested resources with non-root urls paths). Use sub-resources if their life cycle is (loosely) coupled to the main resource, i.e. the main resource works as collection resource of the subresource entities. You should use ⇐ 3 sub-resource (nesting) levels — more levels increase API complexity and url path length. (Remember, some popular web browsers do not support URLs of more than 2000 characters.)

{MUST} use snake_case (never camelCase) for query parameters

See also [118].

{MUST} stick to conventional query parameters

If you provide query support for searching, sorting, filtering, and paginating, you must stick to the following naming conventions:

  • {q}: default query parameter, e.g. used by browser tab completion; should have an entity specific alias, e.g. sku.

  • {sort}: comma-separated list of fields (as defined by [154]) to define the sort order. To indicate sorting direction, fields may be prefixed with + (ascending) or - (descending), e.g. /sales-orders?sort=+id.

  • {fields}: field name expression to retrieve only a subset of fields of a resource. See [157] below.

  • {embed}: field name expression to expand or embedded sub-entities, e.g. inside of an article entity, expand silhouette code into the silhouette object. Implementing {embed} correctly is difficult, so do it with care. See [158] below.

  • {offset}: numeric offset of the first element provided on a page representing a collection request. See [pagination] section below.

  • {cursor}: an opaque pointer to a page, never to be inspected or constructed by clients. It usually (encrypted) encodes the page position, i.e. the identifier of the first or last page element, the pagination direction, and the applied query filters to recreate the collection. See [cursor-based-pagination] or [pagination] section below.

  • {limit}: client suggested limit to restrict the number of entries on a page. See [pagination] section below.