api design guidelines

1. Introduction

This is a design guide for networked APIs, in particular REST and gRPC APIs, shared here to inform internal and external developers and to make it easier for us all to work together.

By providing a set of guidelines, we want to answer many common questions encountered along the way of API development, following RESTful principles and also want to inspire additional discussion and refinement within and among our teams. We seek to define a common resource-oriented design for Infront’s services platform, which should promote API adoption, reduce friction and enable proper usage.

1.1. Usage

Feel free to use these guidelines as a guidance for your own development. Also consider this to be a living, evolving document. We will revise and update based on our learnings and experiences.

1.2. Requirement Levels

The requirement level keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" used in this document are to be interpreted as described in RFC 2119.

1.3. Design Principles

We want to provide RESTful and gRPC APIs, which can be used by internal and external clients, whereas only the gRPC API enables efficient communication between microservices. The RESTful API however enables robust hypermedia REST clients.

This guide borrows from other design guides, which should be introduced briefly in the following. Their updated versions should be considered for possible inclusion in this document.

As discussed in Hypermedia, we figured that providing a complete RESTful API including hypermedia can have beneficial effects for clients, when they make use of it. In order to give us full flexibility in designing the RESTful API we MUST separate it from the gRPC API.

We differ from Zalando’s guidelines, since we SHOULD use hypermedia formats for expressing client side state. Also Zalando’s API is only intended for use by external web clients, not other services unlike most of our APIs.

2. Security

2.1. Endpoints security

Note
Applies to RESTful APIs

Every API endpoint MUST be secured using OAuth 2.0. The following examples show how to specify security definitions in you API:

Listing 1. Swagger 2 example
securityDefinitions:
  # OAuth2 password flow (default)
  OAuth2:
    description: OAuth2 with password flow
    type: oauth2
    authorizationUrl: 'https://idm.eu.infrontfinance.com/auth/realms/vwd-internal/protocol/openid-connect/auth'
    tokenUrl: 'https://idm.eu.infrontfinance.com/auth/realms/vwd-internal/protocol/openid-connect/token'
    flow: password
    scopes:
      read:<service-name>: Access right needed to read from <service-name>
      write:<service-name>: Access right needed to write to <service-name>
      read:<service-name>.<endpoint>: Access right needed to read from the <service-name>.endpoint
      write:<service-name>.<endpoint>: Access right needed to write to the <service-name>.<endpoint>

The keycloak server is used for authorization and requesting a token and reachable under the following (actually dev) address:

Authorization endpoints:

  • "https://idm.staging.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/auth"

  • "https://idm.preview.eu.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/auth"

  • "https://idm.eu.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/auth"

  • "https://idm.preview.ch.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/auth"

  • "https://idm.ch.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/auth"

Token endpoints:

  • "https://idm.staging.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/token"

  • "https://idm.preview.eu.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/token"

  • "https://idm.eu.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/token"

  • "https://idm.preview.ch.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/token"

  • "https://idm.ch.infrontfinance.com/auth/realms/%realm%/protocol/openid-connect/token"

Hint: %realm% is a variable and depends on the application

The example defines OAuth2 with password flow as security standard used for authentication when accessing endpoints; additionally, there are two API access rights defined via the scopes section for later endpoint authorization usage - please see next section.

It makes little sense specifying the flow to retrieve OAuth tokens in the securityDefinitions section, as API endpoints should not care, how OAuth tokens were created. Unfortunately the flow field is mandatory and cannot be ommited. API endpoints SHOULD always set flow: password and ignore this information.

2.2. Access Rights (Scopes)

Every API needs to define access rights, called scopes here, and every endpoint needs to have at least one scope assigned. Scopes are defined by name and description per API specification, as shown in the previous section. Please refer to the following rules when creating scope names:

<api-scope> ::= <api-standard-scope> |            -- should be sufficient for majority of use cases
                <api-resource-specific-scope> |   -- for special security access differentiation use cases
                <api-pseudo-scope>                -- used to explicitly indicate that access is not restricted

<api-standard-scope>          ::= <application-id>.<access-type>
<api-resource-specific-scope> ::= <application-id>.<resource-id>.<access-type>
<api-pseudo-scope>            ::= uid

<application-id> ::= <as defined via STUPS>
<access-type>    ::= read | write           -- might be extended in future
<resource-id>    ::= <free identifier following application-id syntax>

APIs SHOULD stick to standard scopes by default — for the majority of use cases, restricting access to specific APIs (with read vs. write differentiation) is sufficient for controlling access for client types like merchant or retailer business partners, customers or operational staff. Too many fine grained scopes increasing governance complexity without real value add SHOULD be avoided. In some situations, where the API serves different types of resources for different owners, resource specific scopes MAY make sense.

Application ID Resource ID Access Type *Example

fulfillment-order

read

fulfillment-order.read

fulfillment-order

write

fulfillment-order.write

sales-order

sales_order

read

sales-order.sales_order.read

sales_order

shipment_order

read

shipment-order.shipment_order.read

After scope names are defined and the scope is declared in the security definition at the top of an API specification, it SHOULD be assigned to each API operation by specifiying a security requirement.

Listing 2. Swagger 2 example
paths:
  /product-profiles:
    get:
      summary: Retrieves a product profiles list
      security:
        - OAuth2: [read.product_profiles]
      responses:
        200:
          description: ...

In very rare cases a whole API or some selected endpoints may not require specific access control. However, to make this explicit you SHOULD assign the uid pseudo access right scope in this case. It is the user id and always available as OAuth2 default scope.

Hint: you need not explicitly define the "Authorization" header; it is a standard header so to say implicitly defined via the security section.

In cases you want to grant access to certain resources without giving the user access to the keycloak server, API keys SHOULD be used:

Listing 3. Swagger 2 example
securityDefinitions:
  # X-API-Key: abcdef12345
   APIKeyHeader:
     description: Provide API-Key as header
     type: apiKey
     in: header
     name: X-API-Key

   # /path?api_key=abcdef12345
   APIKeyQueryParam:
     description: Provide API-Key as query parameter
     type: apiKey
     in: query
     name: api_key

paths:
  /information:
    get:
      summary: Retrieves information
      security:
        - APIKeyHeader: []
        - APIKeyQueryParam: []
      responses:
        200:
          description: Test

2.3. Security with gRPC

Note
Applies to gRPC APIs.
  • For internal and external APIs, the API MUST be secured with OAuth2. The client has to send the token within the header (Metadata) of the request - so no additional request field has to be defined within the message itself.

The token has to be sent in a metadata field named ACCESS_TOKEN.

3. Resource Oriented Design

Note
Applies to RESTful and gRPC APIs

The architectural style of REST was primarily designed to work well with HTTP/1.1. Its core principle is to define named resources that can be manipulated using a small number of methods. The resources and methods are known as nouns and verbs of APIs. With the HTTP protocol, the resource names naturally map to URLs, and methods naturally map to HTTP methods POST, GET, PUT, PATCH, and DELETE.

In order to keep RESTful and gRPC APIs in sync it is REQUIRED to apply the resource-oriented design also to gRPC services.

3.1. Design Flow

The Design Guide suggests taking the following steps when designing resource-oriented APIs (more details are covered in specific sections below):

  1. Determine what types of resources an API provides.

  2. Determine the relationships between resources.

  3. Decide the resource name schemes based on types and relationships.

  4. Decide the resource schemas.

  5. Attach minimum set of methods to resources.

3.2. Resources

A resource-oriented API is generally modeled as a resource hierarchy, where each node is either a simple resource or a collection resource. For convenience, they are often called as a resource and a collection, respectively.

  • A collection contains a list of resources of the same type. For example, a user has a collection of contacts.

  • A resource has some state and zero or more sub-resources. Each sub-resource can be either a simple resource or a collection resource.

3.2.1. Resource Types

  • Predefined one-off resources for especially important aspects of the application.

    This includes top-level directories of other available resources. Most services expose few or no one-off resources. For example a web site’s homepage. It’s a one-of-a-kind resource, at a well-known URI, which acts as a portal to other resources.

  • A resource for every object exposed through the service.

    One service may expose many kinds of objects, each with its own resource set. Most services expose a large or infinite number of these resources.

  • Resources representing the results of algorithms applied to the data set.

    This includes collection resources, which are usually the results of queries. Most expose an infinite number of algorithmic resources, or they don’t expose any. Algorithmically-generated resources, because they rely on the client providing an arbitrary search string (e.g. an ISIN) or combining unrelated elements (e.g. "issuername = Deka" or "country = Deutschland").

3.3. Methods

The key characteristic of a resource-oriented API is that it emphasizes resources (data model) over the methods performed on the resources (see functionality). A typical resource-oriented API exposes a large number of resources with a small number of methods. The methods can be either the standard methods or custom methods. For this guide, the standard methods are: List, Get, Create, Update, and Delete.

Where API functionality naturally maps to one of the standard methods, that method SHOULD be used in the API design. For functionality that does not naturally map to one of the standard methods, custom methods MAY be used. Custom methods offer the same design freedom as traditional RPC APIs, which can be used to implement common programming patterns, such as database transactions or data analysis.

Whenever it seems tempting to add a method as noun to the API like "subscribe", it is RECOMMENDED to consider defining a new resource, e.g. "subscription".

4. Naming Conventions

Note
Applies to RESTful and gRPC APIs

Since many developers are not native English speakers, one goal of these naming conventions is to ensure that the majority of developers can easily understand an API. It does this by encouraging the use of a simple, consistent, and small vocabulary when naming methods and resources.

  • Names used in APIs SHOULD be in correct American English. For example, license (instead of licence), color (instead of colour).

  • Commonly accepted short forms or abbreviations of long words MAY be used for brevity. For example, API is preferred over Application Programming Interface.

  • Use intuitive, familiar terminology where possible. For example, when describing removing (and destroying) a resource, delete is preferred over erase.

  • Use the same name or term for the same concept, including for concepts shared across APIs.

  • Avoid name overloading. Use different names for different concepts.

  • Avoid overly general names that are ambiguous within the context of the API. They can lead to misunderstanding of API concepts. Rather, choose specific names that accurately describe the API concept. This is particularly important for names that define first-order API elements, such as resources. There is no definitive list of names to avoid, as every name must be evaluated in the context of other names. Instance, info, and service are examples of names that have been problematic in the past. Names chosen should describe the API concept clearly (for example: instance of what?) and distinguish it from other relevant concepts (for example: does "alert" mean the rule, the signal, or the notification?).

  • Carefully consider use of names that may conflict with keywords in common programming languages. Such names MAY be used but will likely trigger additional scrutiny during API review. Use them judiciously and sparingly.

Important
As the REST and gRPC API definitions should be generated by a shared source definition, the naming of both API types depend on each other. Please bear this dependency in mind when you create an API definition.

4.1. Field names

Field name definitions MUST use lower_case_underscore_separated_names. These names will be mapped to the native naming convention in generated code for each programming language.

Field names SHOULD avoid prepositions (e.g. "for"), for example:

  • reason_for_error should instead be error_reason

  • cpu_usage_at_time_of_failure should instead be failure_time_cpu_usage

Field names SHOULD also avoid using postpositive adjectives (modifiers placed after the noun), for example:

  • items_collected should instead be collected_items

  • objects_imported should instead be imported_objects

4.2. Query / Method Parameters

The naming of query parameters (REST) and method parameters (gRPC) should match.

It is REQUIRED to use snake_case (never camelCase) for query/method parameters.

Listing 4. Examples
customer_number, order_id, billing_address
Use Conventional Query / Method Parameters

If you provide query support for sorting, pagination, filtering functions or other actions, you SHOULD use the following standardized naming conventions:

Table 1. Conventional Query Parameters
Parameter Description

limit

to restrict the number of entries. See Pagination section below.

cursor

key-based page start. See Pagination section below.

offset

numeric offset page start. See Pagination section below.

sort

comma-separated list of fields to sort. To indicate sorting direction, fields my prefixed with + (ascending) or - (descending, default), e.g. /sales-orders?sort=+id

fields

to retrieve a subset of fields. See Support Filtering of Resource Fields.

When using query parameters, you SHOULD reference the parameter definitions under https://api.development.vwd.com/definitions/queryparameters.openapi-2.0.yaml.

4.3. gRPC Specific Naming

gRPC specific naming rules regarding packages, interfaces, resources, collections, methods, and messages.

4.3.1. Package names

Package names declared in the API .proto files SHOULD be consistent with Product and Service Names. For example:

package infrontfinance.ms.services.calendar;

An abstract API that isn’t directly associated with a service, should use proto package names consistent with the Product name:

package infrontfinance.ms.services.portfoliomanager;

Java package names specified in the API .proto files must match the proto package names with standard Java package name prefix (com., edu., net., etc). For example:

package infrontfinance.ms.services.portfoliomanager;

// Specifies Java package name, using the standard prefix "com."
option java_package = "com.infrontfinance.ms.services.portfoliomanager";

4.3.2. Collection IDs

Collection IDs should use plural form and lowerCamelCase, and American English spelling and semantics. For example: events, children, or deletedEvents.

4.3.3. Interface names

To avoid confusion with Service Names such as pubsub.infrontfinance.com, the term interface name refers to the name used when defining a service in a .proto file:

// Library is the interface name.
service Library {
  rpc ListBooks(...) returns (...);
  rpc ...
}

You can think of the service name as a reference to the actual implementation of a set of APIs, while the interface name refers to the abstract definition of an API.

An interface name SHOULD use an intuitive noun such as Calendar or Blob. The name SHOULD NOT not conflict with any well-established concepts in programming languages and their runtime libraries (for example, File).

In the rare case where an interface name would conflict with another name within the API, a suffix (for example Api or Service) SHOULD be used to disambiguate.

4.3.4. Method names

A service MAY, in its IDL specification, define one or more RPC methods that correspond to methods on collections and resources. The method names SHOULD follow the naming convention of VerbNoun in upper camel case, where the noun is typically the resource type.

The verb portion of the method name SHOULD use the imperative mood, which is for orders or commands rather than the indicative mood which is for questions.

This is easily confused when the verb is asking a question about a sub-resource in the API, which is often expressed in the indicative mood. For example, ordering the API to create a book is clearly CreateBook (in the imperative mood), but asking the API about the state of the book’s publisher might use the indicative mood, such as IsBookPublisherApproved or NeedsPublisherApproval. To remain in the imperative mood in situations like this, rely on commands such as "check" (CheckBookPublisherApproved) and "validate" (ValidateBookPublisher).

4.3.5. Message names

The request and response messages for RPC methods SHOULD be named after the method names with the suffix Request and Response, respectively, unless the method request or response type is:

  • an empty message (use google.protobuf.Empty),

  • a resource type, or

  • a resource representing an operation

This typically applies to requests or responses used in standard methods Get, Create, Update, or Delete.

4.3.6. Enum names

Enum types MUST use UpperCamelCase names.

Enum values MUST use CAPITALIZED_NAMES_WITH_UNDERSCORES. Each enum value MUST end with a semicolon, not a comma. The first value MUST be named UNSPECIFIED as it is returned when an enum value is not explicitly specified.

enum FooBar {
  // The first value represents the default and must be == 0.
  UNSPECIFIED = 0;
  FIRST_VALUE = 1;
  SECOND_VALUE = 2;
}

4.4. RESTful Specific Naming

4.4.1. Paths

  • MUST use lowercase separate words with hyphens for path segments.

    Listing 5. Example:
    /stock-staticdata/{stock-id}

    This applies to concrete path segments and not the names of path parameters. For example {stock_staticdata_id} would be ok as a path parameter.

  • MUST pluralize resource names.

    Usually, a collection of resource instances is provided (at least API should be ready here). The special case of a resource singleton is a collection with cardinality 1.

  • MAY: Use /api as first path segment (for internal APIs).

  • MAY: Use /public as first path segment. This is used to mark this resource as public (in terms of authentication); it can be accessed without any authentication.

    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 - add "/api" as base path to clearly separate public and non-public API resources.

  • 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.

    Listing 6. Basic URL structure: (authenticated, public and private APIs)
        /{resources}/[resource-id]/{sub-resources}/[sub-resource-id]
    /api/{resources}/[resource-id]/{sub-resources}/[sub-resource-id]
    Listing 7. Basic URL structure: (unauthenticated)
    /public/{resources}/[resource-id]/{sub-resources}/[sub-resource-id]
  • MUST: Use (Non-) Nested URLs

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

    /carts/1681e6b88ec1/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, product-profiles contain a collection of configs; however, configs have globally unique id and some services may choose to access the configs directly, for instance:

    /product-profiles/{pp_id}/configs/{cfg_id}
    /product-profile-configs/{cfg_id}

    To satisfy the basic URL structure, every second path level MUST be a path parameter. So never use the following path style for sub-resources:

    /product-profiles/configs/{cfg_id}
  • All sub-paths SHOULD be valid references

    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 example, all of these paths should be valid:

/customers/12ev123bv12v/addresses/DE_100100101
/customers/12ev123bv12v/addresses
/customers/12ev123bv12v
/customers
  • MUST: Use separate paths for single resources and collections.

    Paths that end on an resource identifier MUST always return a single resource. For example /customers/12ev123bv12v returns only the customer with the given ID.

    To request the collection of all customers, use the path /customers (use pagination if these lists can get large).

  • MUST avoid trailing slashes

    The trailing slash must not have specific semantics. Resource paths must deliver the same results whether they have the trailing slash or not.

4.4.2. HTTP Headers

  • REQUIRED to use hyphenated HTTP headers

  • SHOULD prefer hyphenated-pascal-case for HTTP header fields

  • MAY use standardized headers

  • MUST when using own, non-standardized headers, always use the prefix X-VWD(legacy) or X-INFRONTFINANCE

This is for consistency in your documentation (most other headers follow this convention). Avoid camelCase (without hyphens). Exceptions are common abbreviations like "ID."

Listing 8. Examples
Accept-Encoding
Apply-To-Redirect-Ref
Disposition-Notification-Options
Original-Message-ID
X-VWD-Deeplink
X-INFRONTFINANCE-Dochub

4.5. Single response format

All single responses MUST have the following basic structure:

  • entity (e.g. product_profile) with these attributes:

    • _meta

    • _attributes

    • _links

    • all real attributes of the entity

_meta is an optional property containing additional meta information in a key value map. You MUST use the given definition "EntityMeta" under https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/EntityMeta

_attributes is an optional property and contains additional information for all attributes. You MUST use the given definition "EntityAttributes" under https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/EntityAttributes

_links is an optional property and contains links that lead to further information of a requested resource. You MUST use the given definition "EntityLinks" under https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/EntityLinks

{
  "product_profile": {
    "id": "ca68619b-205d-11e7-b09e-0030486074ca",
    "update_timestamp": "2017-03-20T16:36:30.415Z",
    "isin": "DE0007100000",
    "product_profile_attributes": {...},
    "_meta": {...},
    "_attributes": {...},
    "_links": {
      "self": {
        "name": "string",
        "href": "string",
        "templated": false
      }
    }
  }
}

4.6. Collection response format

All collection responses MUST have the following basic structure:

  • _meta

  • _pagination

  • _data

_meta is an optional property containing additional meta information in a key value map. You MUST use the given definition "Meta" under https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/Meta

_pagination is an optional property and contains information about pagination. You MUST use the given definition "Pagination" under https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/Pagination

_data MUST contain a collection of objects with all information from the requested resource.

Important
Per row you MUST return a map including all requested entities via parameter fields. This is necessary to aggregate/compose results of different services!
{
  "_meta": {...},
  "_data": [
    {
      "product_profile": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        "update_timestamp": "2017-03-20T16:36:30.415Z",
        "product_profile_attributes": {...},
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      },
      "producer_product_profile": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        "isin": "DE0007100000",
        "product_profile_attributes": {...},
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      },
      "security": {
        "instrument_id": 99949,
        "instrument_key": "519000.ETR",
        "isin": "DE0005190003",
        "description": "Bayerische Motorenwerke AG",
        "description_short": "BMW",
        "security_category_code": "CERT",
        "security_group_code": "CERT_BONUS",
        "security_type_code": "CERT_BONUS_CAP",
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      }
    }
  ],
  "_pagination": {
    "index": 0,
    "page_size": 0,
    "self": {
      "name": "string",
      "href": "string",
      "templated": false
    },
    "first": {...},
    "next": {...},
    "prev": {...},
    "last": {...}
  }
}

5. Methods

Note
Applies to RESTful and gRPC APIs

This chapter defines the concept of standard methods for gRPC and how they map to HTTP Operations for REST services. The standard gRPC methods are: List, Get, Create, Update, and Delete.

These methods define a common set of CRUD operations to reduce complexity and increase consistency between APIs.

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

Table 2. Uniform interface
gRPC Method HTTP Mapping HTTP Request Body HTTP Response Body

List

GET <collection URL>

Empty

Resource list

Get

GET <resource URL>

Empty

Resource

Create

POST <collection URL>

Resource

Resource

Update

PUT or PATCH <resource URL>

Resource

Resource

Delete

DELETE <resource URL>

Empty

Empty

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

The response returned from a Delete method that doesn’t immediately remove the resource (such as updating a flag or creating a long-running delete operation) should contain either the long-running operation or the modified resource.

A standard method may also return a long running operation for requests that do not complete within the time-span of the single API call.

The following sections describe each of the standard methods in detail. The examples show the methods defined in .proto files with special annotations for the HTTP mappings.

5.1. Standard Methods

Note
Applies to gRPC APIs only

5.1.1. 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 for data from a single collection that is bounded in size and not cached.

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.

The following code example defines a standard List method that lists a collection of books:

// Lists all the books on a given shelf.
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {}

message ListBooksRequest {
  // The parent resource name, for example, "shelves/shelf1".
  string parent = 1;

  // The maximum number of items to return.
  int32 page_size = 2;

  // The next_page_token value returned from a previous List request, if any.
  string page_token = 3;
}

message ListBooksResponse {
  // The field name should match the noun "books" in the method name.  There
  // will be a maximum number of items returned based on the page_size field
  // in the request.
  repeated Book books = 1;

  // Token to retrieve the next page of results, or empty if there are no
  // more results in the list.
  string next_page_token = 2;
}

5.1.2. Get

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

The following code example defines a standard Get method that gets a specified book:

// Gets the specified book.
rpc GetBook(GetBookRequest) returns (Book) {}

message GetBookRequest {
  // The field will contain name of the resource requested, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

5.1.3. Create

The Create method takes a collection name, a resource, and zero or more parameters. It creates a new resource in the specified collection, and returns the newly created resource.

If the API supports creating resources, it SHOULD have a Create method for each type of resource that can be created.

If the Create method supports client-assigned resource name and the resource already exists, the request SHOULD either fail with error code google.rpc.Code.ALREADY_EXISTS or use a different server-assigned resource name and the documentation should be clear that the created resource name may be different from that passed in.

The following code example defines a standard Create method that creates a book inside a parent shelf:

rpc CreateBook(CreateBookRequest) returns (Book) {}

message CreateBookRequest {
  // The parent resource name where the book to be created.
  string parent = 1;

  // The book id to use for this book.
  string book_id = 3;

  // The book resource to create.
  // The field name should match the Noun in the method name.
  Book book = 2;
}

rpc CreateShelf(CreateShelfRequest) returns (Shelf) {}

message CreateShelfRequest {
  Shelf shelf = 1;
}

5.1.4. Update

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.

If the API accepts client-assigned resource names, the server may allow the client to specify a non-existent resource name and create a new resource. Otherwise, the Update method should fail with non-existent resource name. The error code NOT_FOUND should be used if it is the only error condition.

An API with an Update method that supports resource creation SHOULD also provide a Create method. Rationale is that it is not clear how to create resources if the Update method is the only way to do it.

The following code example defines a standard Update method that updates a specified book:

rpc UpdateBook(UpdateBookRequest) returns (Book) {}

message UpdateBookRequest {
  // The book resource which replaces the resource on the server.
  Book book = 1;

  // The update mask applies to the resource. For the `FieldMask` definition,
  // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
  FieldMask update_mask = 2;
}

5.1.5. Delete

The Delete method takes a resource name and zero or more parameters, and deletes or schedules for deletion the specified resource. The Delete method should return google.protobuf.Empty.

An API SHOULD NOT rely on any information returned by a Delete method, as it CANNOT be invoked repeatedly.

Calls to the Delete method SHOULD be idempotent in effect, but do not need to yield the same response. Any number of Delete requests should result in a resource being (eventually) deleted, but only the first request should result in a success code. Subsequent requests should result in a google.rpc.Code.NOT_FOUND.

The following code example defines a standard Delete method that deletes a specified book:

rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {}

message DeleteBookRequest {
  // The resource name of the book to be deleted, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

5.2. HTTP Verbs

Note
Applies to RESTful APIs only

5.2.1. GET

GET requests are used to read a single resource or query set of resources.

  • GET requests for individual resources will usually generate a 404 if the resource does not exist

  • GET requests for collection resources may return either 200 (if the listing is empty) or 404 (if the list is missing)

  • GET requests MUST NOT have request body payload

  • GET requests on collection resources SHOULD provide a sufficient filter mechanism as well as pagination.

GET with Body

APIs sometimes face the problem, that they have to provide extensive structured request information with GET, that may even conflicts with the size limits of clients, load-balancers, and servers. As we require APIs to be standard conform (body in GET must be ignored on server side), API designers have to check the following two options:

  1. GET with URL encoded query parameters: when it is possible to encode the request information in query parameters, respecting the usual size limits of clients, gateways, and servers, this should be the first choice. The request information can either be provided distributed to multiple query parameters or a single structured URL encoded string.

  2. POST with body content: when a GET with URL encoded query parameters is not possible, a POST with body content must be used. In this case the endpoint must be documented with the hint GET with body to transport the GET semantic of this call.

Note
It is no option to encode the lengthy structured request information in header parameters. From a conceptual point of view, the semantic of an operation should always be expressed by resource name and query parameters, i.e. what goes into the URL. Request headers are reserved for general context information, e.g. FlowIDs. In addition, size limits on query parameters and headers are not reliable and depend on clients, gateways, server, and actual settings. Thus, switching to headers does not solve the original problem.
GET-Examples
  1. Single Resource

{
  "product_profile": {
    "id": "ca68619b-205d-11e7-b09e-0030486074ca",
    "update_timestamp": "2017-03-20T16:36:30.415Z",
    "isin": "DE0007100000",
    "product_profile_attributes": {...},
    "_meta": {...},
    "_attributes": {...},
    "_links": {
      "self": {
        "name": "string",
        "href": "string",
        "templated": false
      }
    }
  }
}
  1. Collection Resource

{
  "_meta": {...},
  "_data": [
    {
      "product_profile": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        "update_timestamp": "2017-03-20T16:36:30.415Z",
        "product_profile_attributes": {...},
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      },
      "producer_product_profile": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        "isin": "DE0007100000",
        "product_profile_attributes": {...},
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      },
      "security": {
        "instrument_id": 99949,
        "instrument_key": "519000.ETR",
        "isin": "DE0005190003",
        "description": "Bayerische Motorenwerke AG",
        "description_short": "BMW",
        "security_category_code": "CERT",
        "security_group_code": "CERT_BONUS",
        "security_type_code": "CERT_BONUS_CAP",
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        }
      }
    }
  ],
  "_pagination": {
    "index": 0,
    "page_size": 0,
    "self": {
      "name": "string",
      "href": "string",
      "templated": false
    },
    "first": {...},
    "next": {...},
    "prev": {...},
    "last": {...}
  }
}

5.2.2. PUT

PUT requests are used to create or update entire resources - single or collection resources. The semantic is best described as please put the enclosed representation at the resource mentioned by the URL, replacing any existing resource.

  • PUT requests are usually applied to single resources, and not to collection resources, as this would imply replacing the entire collection (because of that be careful when using PUT for collections)

  • PUT requests are usually robust against non-existence of resources by implicitly creating before updating

  • on successful PUT requests, the server will replace the entire resource addressed by the URL with the representation passed in the payload (subsequent reads will deliver the same payload)

  • successful PUT requests will usually generate 200 or 204 (if the resource was updated - with or without actual content returned), and 201 (if the resource was created)

  • therefore PUT is idempotent

Note
Resource IDs with respect to PUT requests are maintained by the client and passed as a URL path segment. Putting the same resource twice is required to be idempotent and to result in the same single resource instance. If PUT is applied for creating a resource, only URIs should be allowed as resource IDs. If URIs are not available POST should be preferred.

To prevent unnoticed concurrent updates when using PUT, the combination of ETag and If-(None-)Match header headers should be considered to signal the server stricter demands to expose conflicts and prevent lost updates.

5.2.3. POST

POST requests are idiomatically used to create single resources on a collection resource endpoint, but other semantics on single resources endpoint are equally possible, but discouraged. So POST SHOULD NOT be used for single resources, use PUT instead.

The semantic for collection endpoints is best described as please add the enclosed representation to the collection resource identified by the URL. The semantic for single resource endpoints is best described as please execute the given well specified request on the collection resource identified by the URL.

  • POST request should only be applied to collection resources, and normally not on single resource, as this has an undefined semantic

  • on successful POST requests, the server will create one or multiple new resources and provide their URI/URLs in the response

  • successful POST requests will usually generate 200 (if resources have been updated), 201 (if resources have been created), and 202 (if the request was accepted but has not been finished yet)

  • the server must respond with 409 if the resource already exists (when using with single resource endpoints).

More generally: POST should be used for scenarios that cannot be covered by the other methods sufficiently. For instance, GET with complex (e.g. SQL like structured) query that needs to be passed as request body payload because of the URL-length constraint. In such cases, make sure to document the fact that POST is used as a workaround.

Note
Resource IDs with respect to POST requests are created and maintained by server and returned with response payload. Posting the same resource twice is by itself not required to be idempotent and may result in multiple resource instances. Anyhow, if external URIs are present that can be used to identify duplicate requests, it is best practice to implement POST in an idempotent way.

5.2.4. PATCH

PATCH request are only used for partial update of single resources, i.e. where only a specific subset of resource fields should be replaced. The semantic is best described as please change the resource identified by the URL according to my change request. The semantic of the change request is not defined in the HTTP standard and must be described in the API specification by using suitable media types.

  • PATCH requests are usually applied to single resources, and not on collection resources, as this would imply patching on the entire collection

  • PATCH requests are usually not robust against non-existence of resource instances

  • on successful PATCH requests, the server will update parts of the resource addressed by the URL as defined by the change request in the payload

  • successful PATCH requests will usually generate 200 or 204 (if resources have been updated

    • with or without updated content returned)

In preference order:

  1. use PUT with complete objects to update a resource as long as feasible (i.e. do not use PATCH at all).

  2. use PATCH with partial objects to only update parts of a resource, when ever possible. (This is basically JSON Merge Patch, a specialized media type application/merge-patch+json that is a partial resource representation.)

  3. use PATCH with JSON Patch, a specialized media type application/json-patch+json that includes instructions on how to change the resource.

  4. use POST (with a proper description of what is happening) instead of PATCH if the request does not modify the resource in a way defined by the semantics of the media type.

In practice JSON Merge Patch quickly turns out to be too limited, especially when trying to update single objects in large collections (as part of the resource). In this cases JSON Patch can shown its full power while still showing readable patch requests (see also).

5.2.5. DELETE

DELETE request are used to delete resources. The semantic is best described as please delete the resource identified by the URL.

  • DELETE requests are usually applied to single resources, not on collection resources, as this would imply deleting the entire collection

  • successful DELETE request will usually generate 200 (if the deleted resource is returned) or 204 (if no content is returned)

  • failed DELETE request will usually generate 404 (if the resource cannot be found) or 410 (if the resource was already deleted before)

5.2.6. HEAD

HEAD requests are used retrieve to header information of single resources and resource collections.

  • HEAD has exactly the same semantics as GET, but returns headers only, no body.

  • can be used to check for existance of a resource

5.2.7. OPTIONS

OPTIONS are used to inspect the available operations (HTTP methods) of a given endpoint.

  • OPTIONS requests usually either return a comma separated list of methods (provided by an Allow:-Header) or as a structured list of link templates

Note
OPTIONS is rarely implemented, though it could be used to self-describe the full functionality of a resource.

5.2.8. Fulfill Safeness and Idempotency Properties

An operation MUST be either …​

  • idempotent, i.e. operation will produce the same results if executed once or multiple times (note: this does not necessarily mean returning the same status code)

or…​

  • safe, i.e. must not have side effects such as state changes

Method implementations MUST fulfill the following basic properties:

HTTP method safe idempotent

OPTIONS

Yes

Yes

HEAD

Yes

Yes

GET

Yes

Yes

PUT

No

Yes

POST

No

No

DELETE

No

Yes

PATCH

No

No

6. HTTP Status Codes

Note
This section relates applies to RESTful APIs only.

This guideline groups the following rules for HTTP status codes usage:

  • You MUST not invent new HTTP status codes; only use standardized HTTP status codes and consistent with its intended semantics.

  • You SHOULD use the most specific HTTP status code for your concrete resource request processing status or error situation.

  • You SHOULD provide good documentation in the API definition when using HTTP status codes that are less commonly used and not listed below.

Our list of most commonly used and best understood HTTP status codes:

6.1. Success Codes

Code Meaning Methods

200

OK. RFC 7231

All

201

Created. MAY return created resource. But MUST return created resource when Hypermedia is used. RFC 7231

POST, PUT

202

Accepted. RFC 7231

POST, PUT, DELETE, PATCH

204

No Content. RFC 7231

PUT, DELETE

207

Multi-Status. MUST NOT be used when Hypermedia is applied. RFC 2518

POST

6.2. Redirection Codes

Code Meaning Methods

301

Moved Permanently. RFC 7231

All

303

See Other. RFC 7231

PATCH, POST, PUT, DELETE

304

Not Modified. RFC 7232

GET

6.3. Client Side Error Codes

Code Meaning Methods

400

Bad Request. RFC 7231

All

401

Unauthorized. RFC 7235

All

403

Forbidden. RFC 7231

All

404

Not found. RFC 7231

All

405

Method Not Allowed. RFC 7231

All

406

Not Acceptable. RFC 7231

All

408

Request Timeout. RFC 7231

All

409

Conflict RFC 7231

PUT, DELETE, PATCH

410

Gone. RFC 7231

All

412

Precondition Failed. RFC 7232

PUT, DELETE, PATCH

415

Unsupported Media Type. RFC 7231

PUT, DELETE, PATCH

423

Locked. RFC 2518

PUT, DELETE, PATCH

428

Precondition Required. RFC 6585

All

429

Too Many Requests. RFC 6585

All

6.4. Server Side Error Codes

Code Meaning Methods

500

Internal Server Error. RFC 7231

All

501

Not Implemented. RFC 7231

All

502

Gateway error. RFC 7231

All

503

Service Unavailable. RFC 7231

All

504

Gateway timeout. RFC 7231

All

7. Error Handling

7.1. gRPC Error Handling

Note
This section applies to gRPC APIs only.

7.1.1. Error Model

The error model is logically defined by google.rpc.Status, an instance of which is returned to the client when an API error occurs. The following code snippet shows the overall design of the error model:

package google.rpc;

message Status {
  // A simple error code that can be easily handled by the client. The
  // actual error code is defined by `google.rpc.Code`.
  int32 code = 1;

  // A developer-facing human-readable error message in English. It should
  // both explain the error and offer an actionable resolution to it.
  string message = 2;

  // Additional error information that the client code can use to handle
  // the error, such as retry delay or a help link.
  repeated google.protobuf.Any details = 3;
}

7.1.2. Error Codes

gRPC 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.

7.1.3. 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.

7.1.4. Error Details

gRPC APIs define a set of standard error payloads for error details, which you can find in google/rpc/error_details.proto. These cover the most common needs for API errors. Like error codes, error details should use these standard payloads whenever possible.

Additional error detail types should only be introduced if they can assist application code to handle the errors. If the error information can only be handled by humans, rely on the error message content and let developers handle it manually rather than introducing new error detail types. Note that if additional error detail types are introduced, they must be explicitly registered.

7.1.5. RPC Mapping

Different RPC protocols map the error model differently. For gRPC, the error model is natively supported by the generated code and the runtime library in each supported language. You can find out more in gRPC’s API documentation (for example, see gRPC Java’s io.grpc.Status).

7.1.6. 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.

7.1.7. Handling Errors

Below is a table containing all of the gRPC error codes defined in google.rpc.Code and a short description of their cause. To handle an error, you can check the description for the returned status code and modify your call accordingly.

HTTP RPC Description

200

OK

No error.

400

INVALID_ARGUMENT

Client specified an invalid argument. Check error message and error details for more information.

400

FAILED_PRECONDITION

Request can not be executed in the current system state, such as deleting a non-empty directory.

400

OUT_OF_RANGE

Client specified an invalid range.

401

UNAUTHENTICATED

Request not authenticated due to missing, invalid, or expired OAuth token.

403

PERMISSION_DENIED

Client does not have sufficient permission. This can happen because the OAuth token does not have the right scopes, the client doesn’t have permission, or the API has not been enabled for the client project.

404

NOT_FOUND

A specified resource is not found, or the request is rejected by undisclosed reasons, such as whitelisting.

409

ABORTED

Concurrency conflict, such as read-modify-write conflict.

409

ALREADY_EXISTS

The resource that a client tried to create already exists.

429

RESOURCE_EXHAUSTED

Either out of resource quota or reaching rate limiting. The client should look for google.rpc.QuotaFailure error detail for more information.

499

CANCELLED

Request cancelled by the client.

500

DATA_LOSS

Unrecoverable data loss or data corruption. The client should report the error to the user.

500

UNKNOWN

Unknown server error. Typically a server bug.

500

INTERNAL

Internal server error. Typically a server bug.

501

NOT_IMPLEMENTED

API method not implemented by the server.

503

UNAVAILABLE

Service unavailable. Typically the server is down.

504

DEADLINE_EXCEEDED

Request deadline exceeded. If it happens repeatedly, consider reducing the request complexity.

7.2. REST Error Handling

Use Problem JSON in case of an error is a MUST.

RFC 7807 defines the media type application/problem+json. Operations should return that (together with a suitable status code) when any problem occurred during processing and you can give more details than the status code itself can supply, whether it be caused by the client or the server (i.e. both for 4xx or 5xx errors).

APIs MAY define custom problems types with extension properties, according to their specific needs.

Example

responses:
  ...
  '418':
    description: I am a Teapot
    schema:
      $ref: 'https://api.development.vwd.com/definitions/collections/basedefinitions.yaml#/ProblemResponse'

8. Hypermedia

Note
Applies to RESTful APIs only

The server can guide the client from one application state (in terms of RESTful services: resource) to another by sending links and forms in its representations. R. Fielding’s thesis [rfielding] calls this "Hypermedia as the engine of application state" (HATEOAS). Other publications also talk about just "Hypermedia" or even "Connectedness" [richruby].

In a well-connected service, the client can make a path through the application by following links and filling out forms. In a service that’s not connected, the client must use predefined rules to construct every URI it wants to visit. Therefor the client needs to maintain its own application state. With hypermedia the client state (not the server state) is contained in the server’s representation.

Forms guide the client through the process of modifying resource state with a PUT or POST request, by giving hints about what representations are acceptable.

8.1. Reasons for Hypermedia

  • Clients that rely on rules for URI construction are more brittle

  • No need or out-of-band documentation that describes how the client should work

  • Rules for URI construction change

  • Rules or URI construction can be complex // e.g. in Google Maps tile-based navigation system, which uses coordinates as paramerters. Consider that this is necessary, since the server should contain no state.

8.2. Reasons for Hypermedia in Client-Side Apps

For many people it doesn’t make sense to include hypermedia links and forms in responses because hypermedia clients won’t know what to do with them anyway. This is true and hypermedia clients need human understanding to understand the goals these links represent and to make a choice. The interaction between hypermedia clients and and the human can be summarized in Bill Verplank’s Do-Feel-Know model [verplank]:

Listing 9. Do-Feel-Know model
WHILE
  WITH-USER:
    FEEL(previously-rendered-display)
    KNOW(select-action)
  WITH-MACHINE:
    DO(RUN selected-action on TARGET website and RENDER)
END-WHILE

The human (user) is required to feel (sense) the information presented by the hypermedia client (machine) and needs to know what he wants to do next. The machine is then instructed to run the HTTP action on the resource and present the result to the human.

8.3. Internal vs. External API

This shows in what circumstances hypermedia is a RECOMMENDED choice. Whenever a client application should be developed, that is human assisted, it makes sense to maintain the client side state by using hypermedia links. When an API is designed without a user facing application in mind, hypermedia is less useful but can still be a valuable choice by eliminating error-prone URI construction.

Therefor we recommend…​

8.4. Use REST Maturity Level 2

REQUIRED is a good implementation of REST Maturity Level 2 as it enables us to build resource-oriented APIs that make full use of HTTP verbs and status codes. You can see this expressed by many rules throughout these guidelines, e.g.:

  • Avoid Actions - Think About Resources

  • Keep URLs Verb-Free

  • Use HTTP Methods Correctly

  • Use Meaningful HTTP Status Codes

Although this is not HATEOAS, it should not prevent you from designing proper link relationships in your APIs as stated in rules below.

8.5. Use REST Maturity Level 3

We RECOMMEND to implement REST Maturity Level 3 in APIs for user-facing web applications. HATEOAS comes with additional API complexity without real value in our SOA context where client and server interact via REST APIs and provide complex business functions as part of an SaaS platform.

Our major concerns regarding the promised advantages of HATEOAS:

  • We follow the API First principle with APIs explicitly defined outside the code with standard specification language. HATEOAS does not really add value for SOA client engineers in terms of API self-descriptiveness: a client engineer finds necessary links and usage description (depending on resource state) in the API reference definition anyway.

  • Generic HATEOAS clients which need no prior knowledge about APIs and explore API capabilities based on hypermedia information provided, is a theoretical concept that we haven’t seen working in practise and does not fit to our SOA set-up. The OpenAPI description format (and tooling based on OpenAPI) doesn’t provide sufficient support for HATEOAS either.

  • In practice relevant HATEOAS approximations (e.g. following specifications like HAL or JSON API) support API navigation by abstracting from URL endpoint and HTTP method aspects via link types. So, Hypermedia does not prevent clients from required manual changes when domain model changes over time.

  • Hypermedia make sense for humans, less for SOA machine clients. We would expect use cases where it may provide value more likely in the frontend and human facing service domain.

  • Hypermedia does not prevent API clients to implement shortcuts and directly target resources without discovering them

8.6. Use Common Hypertext Controls

When embedding links to other resources into representations you must use the common hypertext control object. It contains at least one attribute:

  • href: The URI of the resource the hypertext control is linking to. All our API are using HTTP(s) as URI scheme.

In API that contain any hypertext controls, the attribute name href is reserved for usage within hypertext controls.

The schema for hypertext controls can be derived from this model:

HttpLink:
  description: A base type of objects representing links to resources.
  type: object
  properties:
    href:
      description: Any URI that is using http or https protocol
      type: string
      format: uri
  required: [ "href" ]

The name of an attribute holding such a HttpLink object specifies the relation between the object that contains the link and the linked resource. Implementations should use names from the IANA Link Relation Registry whenever appropriate.

Specific link objects may extend the basic link type with additional attributes, to give additional information related to the linked resource or the relationship between the source resource and the linked one.

E.g. a service providing "Person" resources could model a person who is married with some other person with a hypertext control that contains attributes which describe the other person (id, name) but also the relationship "spouse" between between the two persons (since):

{
  "id": "446f9876-e89b-12d3-a456-426655440000",
  "name": "Peter Mustermann",
  "spouse": {
    "href": "https://...",
    "since": "1996-12-19",
    "id": "123e4567-e89b-12d3-a456-426655440000",
    "name": "Linda Mustermann"
  }
}

Hypertext controls are allowed anywhere within a JSON model. While this specification would RECOMMEND HAL, we actually don’t enforce the usage of HAL above other hypermedia formats.

8.7. Use Simple Hypertext Controls for Pagination and Self-References

Hypertext controls for pagination inside collections and self-references SHOULD use a simple URI value in combination with their corresponding link relations (next, prev, first, last, self) instead of the extensible common hypertext control

See Pagination for information how to best represent pageable collections.

9. Data Formats

Note
This section applies to RESTful APIs only.

9.1. Use JSON as the Body Payload

It is REQUIRED to JSON-encode the body payload. The JSON payload must follow RFC-7159 by having (if possible) a serialized object as the top-level structure, since it would allow for future extension. This also applies for collection resources where one naturally would assume an array. See the Pagination section for an example.

9.2. Use other Media Types than JSON

If for given use case JSON does not make sense, for instance when providing attachments in form of PDFs, you SHOULD use another, more sufficient media type. But only do this if you can not transfer the information in JSON.

10. Data Types

Note
This section applies to gRPC APIs only.

10.1. Use Protobuf wrapper types for scalar data types

It is REQUIRED to wrap scalar data types (string, int32, float, etc.) to their respective wrapped data types (StringValue, Int32Value, FloatValue) when it is necessary to check fields for absence.

When using the wrapped types, the generated code has a hasXXX() function to check for the presence of a field

Below is a list with all wrapped data types. See also google/protobuf/wrappers.proto for the definition.

Scalar Wrapper type

bool

BoolValue

int32

Int32Value

int64

Int64Value

uint32

UInt32Value

uint64

UInt64Value

float

FloatValue

double

DoubleValue

string

StringValue

Background: gRPC uses Protobuf version 3 as transport protocol. This version of protobuf does not support optional fields anymore - all fields are required. So for scalar data types, a missing field is filled up with the respective default value, e.g. 0 for int32 or an empty string for strings. When it is important to distinguish between the absence of a field or default value of a field, the only technical way to determine is to use this wrapper types.

11. Performance

11.1. Reduce Bandwidth Needs and Improve Responsiveness

APIs SHOULD support techniques for reducing bandwidth based on client needs. This holds for APIs that (might) have high payloads and/or are used in high-traffic scenarios like the public Internet and telecommunication networks. Typical examples are APIs used by mobile web app clients with (often) less bandwidth connectivity.

Common techniques include:

Each of these items is described in greater detail below.

11.2. Use gzip Compression

It is RECOMMENDED to compress the payload of your APIs responses with gzip, unless there’s a good reason not to - for example, you are serving so many requests that the time to compress becomes a bottleneck. This helps to transport data faster over the network (fewer bytes) and makes frontends respond faster.

Though gzip compression might be the default choice for server payload, the server should also support payload without compression and its client control via Accept-Encoding request header — see also [RFC 7231 Section 5.3.4](http://tools.ietf.org/html/rfc7231#section-5.3.4). The server should indicate used gzip compression via the Content-Encoding header.

11.3. Support Filtering of Resource Fields

Depending on your use case and payload size, you SHOULD significantly reduce network bandwidth need by supporting filtering of returned entity fields. Here, the client can determine the subset of fields he wants to receive via the fields query parameter - example see Google AppEngine APIs partial response:

11.3.1. Unfiltered

GET http://api.example.org/resources/123 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": "cddd5e44-dae0-11e5-8c01-63ed66ab2da5",
  "name": "John Doe",
  "address": "1600 Pennsylvania Avenue Northwest, Washington, DC, United States",
  "birthday": "1984-09-13",
  "partner": {
    "id": "1fb43648-dae1-11e5-aa01-1fbc3abb1cd0",
    "name": "Jane Doe",
    "address": "1600 Pennsylvania Avenue Northwest, Washington, DC, United States",
    "birthday": "1988-04-07"
  }
}

11.3.2. Filtered

GET http://api.example.org/resources/123?fields=name,partner.name HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/json

{
  "name": "John Doe",
  "partner": {
    "name": "Jane Doe"
  }
}

The following listing shows a more complex example and how to use a filter if your response object contains e.g. a security and performance object:

GET http://api.example.org/resources/123?fields=security.isin,security.description,performance.performance_1_year HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/json

{
  "security": {
    "isin": "DE0007100000"
    "description": "Daimler AG NA O.N."
  },
  "performance": {
    "performance_1_year": "13,776"
  }
}

As illustrated by the above examples, field filtering should be done via request parameter "fields" with value range defined by the following BNF grammar.

<fields> ::= <field_path> | <field_path> <FIELD_SEPARATOR> <fields>

<field_path> ::= <field_id> | <field_id> <PATH_SEPARATOR> <field_path>

<field_id> ::= <LETTER> | <LETTER> <LETTER_DIGIT> | <LETTER> <field_id_inner> <LETTER_DIGIT>

<field_id_inner> ::= <DASH_LETTER_DIGIT> | <DASH_LETTER_DIGIT> <field_id_inner>

<FIELD_SEPARATOR> ::= ","

<PATH_SEPARATOR> ::= "."

<DASH_LETTER_DIGIT> ::= <DASH> | <LETTER> | <DIGIT>

<LETTER_DIGIT> ::= <LETTER> | <DIGIT>

<DASH> ::= "-" | "_"

<LETTER> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"

<DIGIT> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

A fields_expression as defined by the grammar describes the properties of an object, i.e. name returns only the name property of the root object. name,partner.name returns the name and partner properties where partner itself is also an object and only its name property is returned.

Note
OpenAPI doesn’t allow you to formally specify whether depending on a given parameter will return different parts of the specified result schema. Explain this in English in the parameter description.

12. Pagination

Note
This section relates applies to RESTful APIs only.

12.1. Support Pagination

Access to lists of data items MUST support pagination for best client side batch processing and iteration experience. This holds true for all lists that are (potentially) larger than just a few hundred entries.

There are two page iteration techniques:

There’re some rules and restrictions:

  • if you don’t specify a limit on your request the default-limit is set to 10

  • you can get max. 100 items per request (with limit=100) 

  • you can get max. 1000 items at all (e.g. with offset=900&limit=100 or offset=975&limit=25). You should provide additional filter if you reach this limit of 1000 items. The number of max items can be increased e.g. for private collections (portfolio-positions, portfolio-accounts…​) where you MUST show more content - BUT DON’T DO THAT PER DEFAULT. If you hit the max-items you can give a hint like "more rows are available - filter your result". An additional export-endpoint can be offered if necessary.

The technical conception of pagination should also consider user experience related issues. As mentioned in this article, jumping to a specific page is far less used than navigation via next/previous page links. This favours cursor-based over offset-based pagination.

The collection should contain additional metadata about the collection or the current page (e.g. index, page_size) when necessary. Links to the single items of you collection should be defined at every item.

You should avoid providing a total count in your API unless there’s a clear need to do so. Very often, there are systems and performance implications to supporting full counts, especially as datasets grow and requests become complex queries or filters that drive full scans (e.g., your database might need to look at all candidate items to count them). While this is an implementation detail relative to the API, it’s important to consider your ability to support serving counts over the life of a service.

If the collection consists of links to other resources, the collection name should use IANA registered link relations as names whenever appropriate, but use plural form.

12.3. Deliver the absolute number of items in the response

Optionally you MAY return the number of items which would be returned by the query max. This means that we either get the actual number of the items. This value is an integer.

  • page_size: the number of items on the current page

  • accessible_size: the maximum number of items accessible via the api (1000 (or higher on special endpoints) or size if size < 1000)

  • size: the total number of items available (but perhaps not reachable if > accessible_size)

12.4. Example

E.g. a service for articles could represent the collection of hyperlinks to an article’s authors like that:

{
  _meta: {...},
  _data: [
    {
      "article": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        ...
        "_meta": {...},
        "_attributes": {...},
        "_links": {
          "self": {
            "name": "string",
            "href": "string",
            "templated": false
          }
        },
      },
    },
    {...},
  ],
  _pagination: {
    "self": {
      "href": "https://.../articles/xyz/authors/",
      "templated": "123e4567-e89b-12d3-a456-426655440000",
      "name": "Kent Beck"
    },
    "first": {...},
    "next": {...},
    "prev": {...},
    "last": {...},
    "index": 0,
    "page_size": 5,
    "accessible_size": 1000,
    "size": 17563
  }
}

13. Grouping

Note
This section relates applies to RESTful APIs only.

13.1. Support Grouping

Access to lists of data items CAN support grouping for best client side drill down into groups. You only have to implement that if requested.

If you want to support grouping you MUST do it that way.

13.2. Request a group

You MUST add the optional query parameter groupings to the api request. You MAY have multible groupings at one request.

13.3. Group Responses

The group result items are of the same type as the not grouped list items.

Every single item in the list MUST have _meta (Hashmap of meta data) and _links.

Only the properties, whitch are grouped are filled. All other are empty.

You MUST fill into the _meta the count value, that contains the size of the elements in that group.

You Must fill the _links' with a `self link, that shows how to drill down into that group.

13.4. Examples

13.4.1. Flat List

E.g. a service for articles with the authors name could response the flat list like that:

Request

https://.../articles

Response

{
  _meta: {...},
  _data: [
    {
      "article": {
        "id": "ca68619b-205d-11e7-b09e-0030486074ca",
        "author": "John"
        ...
        "_meta": {...},
        "_links": {...}
      },
    {
      "article": {
        "id": "ca68619b-205d-11e7-b09e-0030486074cb",
        "author": "John"
        ...
        "_meta": {...},
        "_links": {...}
      },
    {
      "article": {
        "id": "ca68619b-205d-11e7-b09e-0030486074cc",
        "author": "Paul"
        ...
        "_meta": {...},
        "_links": {...}
      },
    },
    {...},
  ],
  _pagination: {...}
}

13.4.2. Grouped List

Request

https://.../articles?groupings=author

Response

{
  _meta: {...},
  _data: [
    {
      "article": {
        "author": "John"
        "_meta": {
          "count": 2
        },
        "_links": {
          "self": {
            "name": "self",
            "href": "https://.../article?author=John",
            "templated": false
          }
        },
      },
    },
    {
      "article": {
        "author": "Paul"
        "_meta": {
          "count": 1
        },
        "_links": {
          "self": {
            "name": "self",
            "href": "https://.../article?author=Paul",
            "templated": false
          }
        },
      },
    },
  ],
  _pagination: {...}
}

14. Common Headers

This section describes a handful of headers, which we found raised the most questions in our daily usage, or which are useful in particular circumstances but not widely known.

14.1. Use Content Headers Correctly

REQUIRED are content or entity headers are headers with a Content- prefix. They describe the content of the body of the message and they can be used in both, HTTP requests and responses. Commonly used content headers include but are not limited to:

  • Content-Disposition can indicate that the representation is supposed to be saved as a file, and the proposed file name.

  • Content-Encoding indicates compression or encryption algorithms applied to the content.

  • Content-Length indicates the length of the content (in bytes).

  • Content-Language indicates that the body is meant for people literate in some human language(s).

  • Content-Location indicates where the body can be found otherwise.

  • Content-Range is used in responses to range requests to indicate which part of the requested resource representation is delivered with the body.

  • Content-Type indicates the media type of the body content.

14.2. Use Content-Location Header

The Content-Location header is OPTIONAL and can be used in successful write operations (PUT, POST or PATCH) or read operations (GET, HEAD) to guide caching and signal a receiver the actual location of the resource transmitted in the response body. This allows clients to identify the resource and to update their local copy when receiving a response with this header.

The Content-Location header can be used to support the following use cases:

  • For reading operations GET and HEAD, a different location than the requested URI can be used to indicate that the returned resource is subject to content negotiations, and that the value provides a more specific identifier of the resource.

  • For writing operations PUT and PATCH, an identical location to the requested URI, can be used to explicitly indicate that the returned resource is the current representation of the newly created or updated resource.

  • For writing operations POST and DELETE, a content location can be used to indicate that the body contains a status report resource in response to the requested action, which is available at provided location.

Note
When using the Content-Location header, the Content-Type header has to be set as well. For example:
GET /products/123/images HTTP/1.1

HTTP/1.1 200 OK
Content-Type: image/png
Content-Location: /products/123/images?format=raw

14.3. Consider using ETag together with If-(None-)Match header

When creating or updating resources it MAY be necessary to expose conflicts and to prevent the lost update problem. This can be best accomplished by using the ETag header together with the If-Match and If-None-Match. The contents of an ETag: <entity-tag> header is either (a) a hash of the response body, (b) a hash of the last modified field of the entity, or (c) a version number or identifier of the entity version.

To expose conflicts between concurrent update operations via PUT, POST, or PATCH, the If-Match: <entity-tag> header can be used to force the server to check whether the version of the updated entity is conforming to the requested <entity-tag>. If no matching entity is found, the operation is supposed a to respond with status code 412 - precondition failed.

Beside other use cases, the If-None-Match: header with parameter * can be used in a similar way to expose conflicts in resource creation. If any matching entity is found, the operation is supposed a to respond with status code 412 - precondition failed.

The ETag, If-Match, and If-None-Match headers definitions can be used from https://api.development.vwd.com/definitions/headers.openapi-2.0.yaml

15. Versioning

15.1. Practice

Versioning MUST NOT be used.

Sometimes APIs can undergo changes without causing major problems, because Hypermedia connected RESTful APIs use link relations to hide changes in the URL format or because the message format of gRPC APIs allow for optional fields. Breaking changes are changes introduced into the resource, request and response design that cause errors on the consumer side.

So it is RECOMMENDED to rely on backwards-compatible changes in order to avoid unnecessary new endpoints and force users of the API to update. gRPC and RESTful APIs have different capabilities in terms of backwards compatibility so we have to differ between those in the following.

15.2. Deprecation

In order to make it transparent for the consumer how long an API will be supported it is REQUIRED to make a statement how long APIs will be maintained, when a newer version under a new endpoint is released. Old endpoints MUST be marked as deprecated.

15.3. RESTful Compatibility

15.3.1. Backwards-compatible changes

  • Adding of properties is backwards compatible if the added fields have a valid absent value semantic and the PATCH instead of PUT is used to update a resource. Otherwise the PUT semantics are violated.

  • Removing fields is a breaking change, since the passed data cannot be consumed by the server properly.

  • Adding of properties to the request body is backwards compatible if the added fields have a valid absent value semantic and the PATCH instead of PUT is used to update a resource. Otherwise the PUT semantics are violated.

  • Removing fields from the request body is a breaking change, since the passed data cannot be consumed by the server properly.

15.3.2. Backwards-incompatible changes

  • Changes in the URL format (resource path, query parameters) introduce a new API and thus a new version when consumers do not rely on Hypermedia.

  • Changes in the request and response body might possibly break a consumer.

JSON and XML request and response bodies might contain non backwards compatible changes if the client is not designed in a way to deal with added or removed properties.

15.4. gRPC Compatibility

The detailed list of gRPC backwards-compatible and non backwards-compatible can be obtained from the Google Cloud APIs Design Guide which can be applied to all gRPC APIs.

Appendix A: Glossary

A glossary term

The corresponding (indented) definition.

Appendix B: Literature and references

  • [rfielding] Roy Thomas Fielding. Architectural Styles and the Design of Network-based Software Architectures. Dissertation. 2000.

  • [richruby] Leonard Richardson & Sam Ruby. RESTful Web Services. O’Reilly. 2007.

  • [verplank] Bill Verplank. Interaction Design Sketchbook. 2009.

  • [apigee] Brian Mulloy. Web API Design. Apigee. 2009.

Appendix D: Changelog

v1.0.0

Promoted current status as first official version

v0.9.0 (draft)

Added collections types, included base definitions as served schemata

v0.2.0 (draft)

Reviewed and partially revised by API team

v0.1.0 (draft)

Initial API spec based on Google gRPC and Zalando REST API guidelines