REST API Standard
In order to provide an API platform that is straightforward to consume, we would like to ensure that all of the APIs in our platform respond to consumers in a consistent way. This standards cover the verbs an API should accept and the responses returned by restful APIs. This includes which response codes to use (and when), the format of the body to return, and how to provide the type of that body.
The Standard
Protocol
Endpoints MUST accept different http methods so MAY support any or all of GET, POST, PUT, DELETE & OPTIONS. GET MUST be idempotent and should be side effect free. PUT MUST be idempotent. Endpoints MUST NOT use PATCH. An endpoint MUST support JSON in requests as denoted by request headers. More types MAY be supported as required. Endpoints MUST support returning data in JSON as denoted by the request headers. More types MAY be supported.
Http Status Codes
All APIs, when returning a response MUST use an appropriate http status code that indicates the outcome of the request in the 2xx-5xx range. Apis MUST NOT use the 100 range. Apis MUST NOT use the extended or webdav ranges. Apis MUST ONLY use codes listed in https://datatracker.ietf.org/doc/html/rfc9110. Generally:
- 2xx - the request was successful
- 3xx - redirect the client
- 4xx - the client must modify their request or change their behaviour before trying again
- 5xx - the request did not work due to an error on the server. The client may retry without changing their request.
Response Bodies
There will be many times when an API needs to return a body to provide information to the client. It might be in response to a GET request, to represent the outcome of a mutation, or to provide further information about an error.
If an API returns a body, it MUST include a content-type to allow the client to parse the response. The API MUST return a json payload, unless otherwise negotiated with the client.
The API MUST provide a suitably name-spaced vendor specific content-type of the response to indicate the return type, so that the client will understand how to parse it. This type MUST be placed in the response content-type.
The content-type MUST contain a lowercase domain with the spaces removed - e.g. customerorder. The domain MUST come from the list approved by the technology standards community. The rest of the response type will be used to uniquely identify the type: e.g. vnd+wmp.price.quote.quotation".
All types returned by an API MUST be specified in the api's openapi definition.
Errors
Errors MUST follow the same scheme as above.
The response MUST NOT contain a numerical error code. The response MUST NOT contain textual problem descriptions/resolution steps. The response MUST NOT contain a stack trace or other potentially sensitive information.
An example of an error:
HTTP/1.1 400 Bad Request
Date: Mon, 25 Dec 2022 12:28:53 GMT
Content-Type: application/vnd+wmp.price.quote.nopriceforproduct+json
{
"productId" : "00012345678905",
"locationId" : "alocationuuid",
"currency": "GBP"
}
Apis MUST NOT put informational free text (such as descriptions) into error responses. All the information the client needs to understand any issues that have happened must appear as part of the typed fields. E.g. if there is a validation failure:
HTTP/1.1 400 Bad Request
Date: Mon, 26 Dec 2022 12:19:53 GMT
Content-Type: application/vnd+wmp.price.quote.insufficientdataprovided+json
{
"missingFields" : ["currency", "locationId"]
}
Handling Failures From a Dependency
4xx Errors from Dependent Services
If the API is calling another api, and that API returns a 4xx error.
- The client needs to know what happened
- The client needs to know if there are steps to resolve any 4xx errors.
- The API needs to be able to handle errors from its dependencies
- We don't expect a client to understand types from an API it does not explicitly consume
Worked Example(s)
Example 1
The client adds a product id to an order via the customer order api. Customer Order calls the product API with the product code to retrieve the data. The product API cannot find the product.
Response 1.1
HTTP/1.1 404 Not Found
404 Not Found
Response 1.2
HTTP/1.1 400 Bad Request
Content-Type: application/vnd+wmp.customerorder.unknownproduct+json
{
"productId" : "00012345678905"
}
Example 2
This example occurs when a product code is added to customer order, and customer order receives an error when trying to add the item to a quote, as the price API does not have a price for that product.
Response 2.1
HTTP/1.1 400 Bad Request
Content-Type: application/vnd+wmp.price.quote.nopriceforproduct+json
{
"productId" : "00012345678905",
"locationId" : "alocationuuid",
"currency": "GBP"
}
Response 2.2
HTTP/1.1 400 Bad Request
Content-Type: application/vnd+wmp.customerorder.unpriceditemcannotbeaddedtoorder+json
{
"productId" : "00012345678905",
"locationId" : "alocationuuid",
"currency": "GBP"
}
500 Errors from Dependent Services
If an API is calling another critical api, and that API returns a 500 error, and a suitable number of retries have been made, then the API MUST return a 5xx error to the client.
If you have an open circuit breaker to the dependent service, then you MUST return a 500 error to the client.
The client can then decide if they want to retry the request, or if they want to give up.