This document is under active development and discussion!

If you find errors or omissions in this document, please don’t hesitate to contact us. New contributors are always welcome!

For brevity most URIs in the examples are shortened, but in reality URIs are always in absolute notation.

1. Introduction

This style guide for RESTful services is a collaborative effort by several Belgian government institutions. It aims to improve compatibility between RESTful services offered by government institutions or any other organization adopting the guidelines.

It is a living document, meant to be updated when new interoperability issues arise or when REST-related standards evolve.

The main benefit for choosing RESTful services is to increase flexibility and to offer web service support to client platforms not able to communicate using SOAP web services.

  • REST is the defacto standard to communicate with web services from JavaScript and native mobile applications.

  • While SOAP is strictly linked to XML and needs complex standards (MTOM, Soap-with-Attachments) to work with other media formats, RESTful services can support this natively.

  • WS-* specifications added to SOAP are often overly complex and redundant to the possibilities over the underlying communication protocol (e.g. HTTP).

  • REST has become the industry standard for developing APIs on the web (Google, Facebook, Amazon, Twitter, etc).

This guide does not include guidelines on how to secure REST APIs. These are under development by the REST Security Working Group, based on the OpenID Connect and OAuth 2.0 standards. It also doesn’t include any specifications on how to represent common business data either (e.g. social security number, address, …​), which are being worked on by the REST Functional Working Group.

2. Changelog

3. REST API

3.1. Uniform interface

The REST uniform interface embraces all aspects of the HyperText Transfer Protocol, version 1.1 (HTTP/1.1) including its request methods, response codes, and message headers.​

The REST uniform interface is based on three fundamental elements:

  1. Resources – How can we express where the data is being transferred to or from?

  2. HTTP Methods – What are the protocol mechanisms used to transfer the data?

  3. Media Types – What type of data is being transferred?

3.2. API

Rule 1: API

An API bundles a set of resources which are functionally related and form an autonomous unit of business logic.

Compare an API to the concept of Services in service-orientation or the concept of Bounded Contexts in Domain-Driven Development.

Rule 2: API format

URI = https://host/pathPrefix/apiName/vmajorVersion/resources

example: https://services.socialsecurity.be/REST/employerProfile/v1/profiles

https://

Services are at least secured on transport level using a one-way TLS connection. The implicit port is 443.

host

Hostname is determined by the environment where the service is deployed

pathPrefix

Optional path prefix to discriminate between REST APIs and other resources on the same host. (example: /REST)

apiName

Name of the API that groups a functional consistent set of resources. The API name consists of one or multiple path segments written in lowerCamelCase (example: /referenceData/geography).

majorVersion

Major version numbers are integers and start at 1. See API versioning.

resources

All path segments identifying the actual resources.

3.3. Richardson Maturity Model

The Richardson Maturity Model (developed by Leonard Richardson) breaks down the principal elements of a RESTful interface into three steps. In order to be compliant to the uniform interface as described by Roy Fielding, all three levels must be fulfilled.

  1. Resources — Level 1 tackles the question of handling complexity by using divide and conquer, breaking a large service endpoint down into multiple resources, each represented by a unique URI.

  2. HTTP Methods — Level 2 introduces a standard set of verbs so that we handle similar situations in the same way, removing unnecessary variation.

  3. Hypermedia controls — Level 3 introduces discoverability, providing a way of making a protocol more self-documenting.

Rule 3: Maturity level

All REST services MUST at least respect level 2 and desirably achieve level 3.

References

4. Resources

A REST APIs exposes a set of resources. Resources model the data and functionality of a system. A resource can be data, processing logic, files, or anything else that a service may have access to.

4.1. Resource URI

Any resource is uniquely identified by a Uniform Resource Identifier or URI (RFC 3986​).

URI = scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]​
​example: https://api.socialsecurity.be/employers/406798006​?lang=nl

URI must be intuitively structured.

Rule 4: URI notation

The URI notation MUST use lowerCamelCase to enhance readability and to separate compound names. As lowerCamelCase is used for JSON property names as well, the casing is consistent throughout the API.

Example 1. URI notation

​​GOOD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​/331
​BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/Social_Secretariats​/331
BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/social-secretariats​/331

Rule 5: URI extensions

The URI SHOULD NOT contain a file extension.

A notable exception to this rule is the swagger/OpenAPI file (see doc resource).

Instead, look at how to express Media Types using HTTP headers.

Example 2. URI extensions

GOOD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​/331
​BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​/331.json

Rule 6: Query parameters

The query part of a URI may be used to filter the resource output.

The query component of a URI contains a set of parameters to be interpreted as a variation or derivative of the resource. The query component can provide clients with additional interaction capabilities such as ad hoc searching and filtering.

Example 3. Query parameters

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​?q=sdworx
Filter the resource collection using a search string.

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331​?select=name&select=address
Filter the resource properties to the ones specified in the select query parameter.

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331​?lang=nl
Only return translatable properties in dutch.

Rule 7: Multiple values for the same query parameter

When a single query parameter can have multiple values, the parameter SHOULD be repeated for each value.

Example 4. Multi-valued parameter
OpenAPI definition
parameters:
- name: embed
  in: query
  type: string
  collectionFormat: multi
  enum:
  - employees
  - mainAddress

4.2. Resource Archetypes

There are two different resource archetypes:

5. Document

A document resource is a singular concept that is referring to a business entity or object instance. A document’s state representation typically includes both fields with values and links to other related resources.

Child resources

A document may have child resources that represent its specific subordinate concepts. Most document resources are contained in a parent collection resource and define a variable path segment with identity key.

collection

When defining child resources, stick to concepts within the same API. An employer resource could contain child concepts as employees, debts, taxes, mandates, declarations, risks, but putting all these different concepts below a single document resource becomes unmanageable. In that case prefer associations and create links to other APIs from the employer resource.

Identity key

The identity key is preferably a natural business identifier, uniquely identifying the business resource. If such key does not exist, a surrogate or technical key (like UUID) can be used.

Especially for unsecured resources, avoid technical keys that are easy to guess (for example sequential identifiers)
Don’t use database-generated keys as identity keys in your public API to avoid tight coupling between the database schema and API. Having the key independent of all other columns insulates the database relationships from changes in data values or database design (agility) and guarantees uniqueness.
Rule 8: Identifier name

The identifier should be named id unless there already exists another standardized name for the business identifier.

In a JSON representation, it SHOULD NOT be prefixed unless the identifier is not of the resource on which the operation applies and there’s ambiguity.

If the identifier is used as path parameter, it should be prefixed by the resource type.

Example 6. Identifier name
GET /stores/{storeId}/orders/{orderId} (1)

{
  "id": 123,  (2)
  "clientId": 456, (3)
  "store": {
     "id": 789, (4)
     "href": "/stores/789"
  }
}
1 path parameter: prefixed with resource type
2 use id as JSON property name for the resource being consulted
3 identifier doesn’t refer to the consulted resource. It would be ambiguous with the order’s id.
4 Clearly refers to the store’s id, so no prefix required.

enterpriseNumber is a standard business name for the CBE enterprise identifier, so it should be used instead of id:

GET /enterprises/{enterpriseNumber}
{
   "enterpriseNumber":  "0202239951"
}

5.1. Consult

Example 7. Consulting a document

​​​​​​​​​GET

/employers/{employerId}

Consult a single employer

​​​Parameters

employerId

path-param

CBE enterprise number uniquely identifying the employer.

Response

body

The response properties and links to other resources.

{
  "self": "/employers/93017373",
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "company": {
    "cbeNbr": 202239951,
    "href": "/companies/202239951"
  }
}

Response codes ​​

200

OK

Default success code if the document exists

​​

304

Not Modified

Used for consultations with conditional requests.

400

Bad request

The dynamic path segment containing the identity key has a wrong data format

{
 "instance": "63b68bfa-bcdc-40f1",
 "title": "Bad Request",
 "code": 400,
 "detail": "The input message is incorrect",
 "invalidParams": [
  {
   "in": "path",
   "name": "employerId",
   "reason": "This value should be numeric",
   "value": "abc"
  }
 ]
}

404

Not Found

The document resource does not exist. ​

Rule 9: Retrieve partial resource representation

The select query parameter is reserved to return a resource representation with only the specified properties.

The value of this parameter SHOULD follow this BNF grammar:

<selects>            ::= [ <negation> ] <selects_struct>
<selects_struct>     ::= "(" <select_items> ")"
<select_items>       ::= <select> [ "," <select_items> ]
<select>             ::= <select_name> | <selects_substruct>
<selects_substruct>  ::= <select_name> <selects_struct>
<select_name>        ::= <dash_letter_digit> [ <select_name> ]
<dash_letter_digit> ::= <dash> | <letter> | <digit>
<dash>              ::= "-" | "_"
<letter>            ::= "A" | ... | "Z" | "a" | ... | "z"
<digit>             ::= "0" | ... | "9"
<negation>          ::= "!"
Example 8. Select query parameter
GET /employers/93017373?select=(name)
{
  "self": "/employers/93017373?select=(name)",
  "name": "Proximus"
}

Note that parentheses around the value of the select parameter are required, even when selecting a single property.

This notation can also be used for nested properties:

GET /employers/93017373?select=(name,address(street(name,code)))
{
  "self": "/employers/93017373",
  "name": "Proximus"
  "address": {
    "street": {
      "name": "Koning Albert II laan",
      "code": 2177
    }
  }
}

5.2. Update

Updating a resource may be done in one of several ways. One and only one of following patterns should be chosen per resource, unless forced by a backwards compatible change.

In order of preference:

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

    This option is preferred when clients are likely to always take into account the entire resource representation. If a client ignores some of a resource’s properties returned by a consultation, they are likely to be omitted from the PUT request and thus lost. This scenario may occur when new properties were added during the API lifecycle. In this case, use of PUT isn’t advised.

  2. Use PATCH with partial objects to only update parts of a resource, whenever possible, using the JSON Merge Patch standard.

    JSON Merge Patch is limited however, e.g. it doesn’t allow for an update of a single element of an array. If this proves to be an issue, this might however indicate that the array elements might be beter modeled as seperate subresources.

  3. use POST on a child resource instead of PATCH if the request does not modify the resource in a way defined by the semantics of the media type. See [non-CRUD operations] for more information.

Use of the JSON Patch standard, an alternative to JSON Merge Patch, is not recommended, as it proves to be difficult to implement.

5.2.1. Full update

Use PUT when you like to do a complete update of a document resource. All values are replaced by the values submitted by the client.

Example 9. PUT on a document resource
PUT http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/employers/93017373 HTTP/1.1

{
    "employerId": 93017373,
    "name": "Belgacom"
}

​​​​​​​​​PUT

/employers/{employerId}

Replace the entire employer resource with the client data. This implies a full update of the resource. Via PUT the client submits new values for all the data.

Request

body

Full representation of the resource to persist.

​​​Parameters

employerId

path-param

CBE enterprise number uniquely identifying the employer.

Response

body

empty or a message indicating success

{
    "employerId": 93017373,
    "name": "Belgacom"
}

Response codes ​​

200

OK

Success code with resource after update echoed

​​

204

No Content

Success code without returning the resource

400

Bad request

The input data is not valid according the data schema.

404

Not Found

The resource does not exist and thus cannot be updated. ​

409

Conflict

The client data is in conflict with the data on the server e.g. optimistic locking issues. ​

5.2.2. Partial update

Use PATCH when you like to do a partial update of a document resource.

The PATCH message MUST be conform to the JSON Merge Patch (RFC 7386) specification:

  • JSON properties in the request overwrite the ones in the previous resource state

  • properties with value null in the request are removed from the resource

  • properties not present in the request are preserved

APIs should support both the MIME type of JSON merge patch application/merge-patch+json as the generic application/json JSON mime type. As JSON Merge Patch requests can not be fully specified as an OpenAPI data type, a MergePatch marker type should be used, defined in common-v1.yaml.

Example 10. JSON merge patch

​​​​​​​​​PATCH

/employers/{employerId}

Performs a partial update of an existing employer.

Request

body

JSON Merge Patch

{
  "bankrupt": false,
  "bankruptDate": null
}

​​​Parameters

employerId

path-param

CBE enterprise number uniquely identifying the employer.

Response

body

empty or the complete resource after applying PATCH

{
    "employerId": 93017373,
    "name": "Belgacom",
    "bankrupt": false
}

Response codes ​​

200

OK

Success code with resource after applying PATCH returned

​​

204

No Content

Success code without returning the resource

400

Bad request

The input data is not valid according the data schema.

404

Not Found

The resource does not exist and thus cannot be updated. ​

409

Conflict

The client data is in conflict with the data on the server e.g. optimistic locking issues. ​

5.3. Remove

Use DELETE when you like to delete a document resource.

Example 11. Deleting a document resource

​​​​​​​​​DELETE

/employers/{employerId}

Deletes an employer.

​​​Parameters

employerId

path-param

CBE enterprise number uniquely identifying the employer.

Response

body

empty or a message indicating success

204 No Content

or​​​

200 OK
{
 "message": "Employer didn't exist"
}

Response codes ​​

204

No Content

Default success code if the delete succeeded

​​

200

OK

Success code with additional message

400

Bad request

The input data is not valid according the data schema.

404

Not Found

The resource does not exist and thus cannot be updated. ​

5.4. Non-CRUD operations

Rule 10: Non-CRUD operations

If a business action isn’t a standard CRUD operation on a resource, it is RECOMMENDED to model it as a child resource.

The HTTP method SHOULD be:

  • POST for actions with side effects (state change) or actions without side effects but requiring a request body

  • GET for idempotent actions without side effects

For actions with side effects, the child resource’s name is RECOMMENDED to be a noun describing the intent of the action rather than the action itself (verb).

Example 12. Non-CRUD POST operation

​​GOOD: POST /account/123/withdrawals
​BAD: POST /account/123/withdraw

Using a noun ensures extensiblity, for example the API can be extended to consult a history of all withdrawals executed on the account.

Example 13. Non-CRUD GET operation

GET /convertMoney?from=EUR&amount=45&to=USD

5.5. Long running tasks

Some operations need to be performed asynchronously, as they take too long to complete.

Rule 11: Long running tasks

Long running tasks MUST be represented as a resource. The task resource is created using a POST action returning a 202 Accepted response containing the URL of the task in the Location HTTP header. It can then be fetched by the client to get the processing status of the task.

When GETting the task resource, the response can be:

  • Still processing: status 200 OK and a representation of the task’s current status

  • Success: status 303 See Other with the Location header containing the URL of the task’s output.

  • Failure: status 200 OK with a representation of the task’s status, including the reason of the failure

Variations on the above may be required, e.g. if the task has no output, the response on success may be 200 OK without a Location header. The schema common-v1.yaml defines the representation of a task’s status.

Example 14. Long running task

Submitting the task

POST /images/tasks

HTTP/1.1 202 Accepted
Content-Type: application/json;charset=UTF-8
Location: http://www.example.org/images/tasks/1
Date: Sun, 13 Sep 2018 01:49:27 GMT
{
  "self": "/images/tasks",
  "status": {
    "state": "processing",
    "pollAfter": "2018-09-13T01:59:27Z"
  }
}

The response 202 Accepted status indicates that the server accepted the request for processing. pollAfter hints when to check for an updated status at a later time.

Getting the processing status

GET /images/tasks/1

When the server is still processing the task

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "processing",
    "pollAfter": "2018-09-13T02:09:27Z"
  }
}

When processing has completed

HTTP/1.1 303 See Other
Location: http://www.example.org/images/1
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "done",
    "completed":"2018-09-13T02:10:00Z",
  }
}

The Location header refers to the result of the task.

In case of failure during processing

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "failed",
    "completed":"2018-09-13T02:10:00Z",
    "problem": {
      "instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
      "title": "Bad Request",
      "status": 400,
      "type": "http://example.org/errors/invalidImageFormat",
      "detail": "Invalid image format"
    }
  }
}

Note that the status code is 200 OK as the retrieval of the task’s status succeeded. The cause of failure is represented using an embedded Problem object, as defined in Error handling.

6. Collection

A collection resource is a server-managed list of document resources.

Rule 12: Collection name

A plural noun SHOULD be used for collection names, for example 'employers' or 'people'.

6.1. Consult

Rule 13: Representation of a collection

The representation of a collection MUST contain a list of links to child resources:

  • A query on a collection MUST contain an items property with an array of objects, each one representing an item in the collection.

  • In these objects, a href property MUST be present with a link to the resource.

  • The unique business identifier SHOULD be present for each item.

  • Each item object MAY be extended with some key business properties, needed for display in a master view.

  • In case the collection is empty, the items property MUST have an empty array as value.

  • The title attribute MAY be used to provide a human-readable description for an item, usable as display text for the link.

  • total is the reserved word for all the items in the collection result.

A collection resource SHOULD always return a JSON object as top-level data structure to support extensibility. Do not return a JSON array, because the moment you like to add paging, hypermedia links, etc, your API will break.
Table 1. Response codes

200

OK

Default response code, also when the collection is empty

Table 2. Query parameters

sort

Multi-value query param with list of properties to sort on. Direction is ascending by default. To indicate descending, prefix property with -.

?sort=-name

Example 16. Consulting a collection
Response
{
 "self": "http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/employers",
 "items":[
   {
     "href":"/employers/93017373",
     "title":"Belgacom",
     "nssoNbr": 93017373,
     "cbeNbr": 202239951
   },
   {
     "href":"/employers/20620259",
     "title":"Partena VZW",
     "nssoNbr": 20620259,
     "cbeNbr": 409536968,
   }
 ],
 "total":2
}
JSON data types
EmployersResponse:
  allOf:
  - $ref: common/v1/common-v1.yaml#/definitions/SelfLink
  - type: object
    properties:
      items:
        type: array
        items:
          $ref: "#EmployerLink"
      total:
        type: integer
EmployerLink:
  allOf:
  - $ref: common/v1/common-v1.yaml#/definitions/HttpLink
  - type: object
    properties:
      nssoNbr:
        $ref: organization/identifier/v1/organization-identifier-v1.yaml#/definitions/NssoNbr
      cbeNbr:
        $ref: organization/identifier/v1/organization-identifier-v1.yaml#/definitions/CbeNbr
Rule 14: Getting full representations of collection items

When the collection items contain few data, you may want to retrieve them in full when getting the collection. In this case, the full representations MUST be included in an 'embedded' property as described in Embedding resources.

Example 17. Consulting a collection with embedded items
Response
{
 "self": "/appendices/employerclasses?embed=items",
 "items": [
  {
   "value": "0",
   "href": "/appendices/employerclasses/0"
  }, {
   "value": "2",
   "href": "/appendices/employerclasses/2"
  }
 ],
 "total":2,
 "embedded": {
   "/appendices/employerclasses/2": {
     "self": "/appendices/employerclasses/2",
     "value": "2",
     "description": {
       "nl": "Bijzondere categorie voor werkgevers die voor hun arbeiders een speciale bijdrage verschuldigd zijn.",
       "fr": "Catégorie particulière pour les employeurs redevables pour les ouvriers d’une cotisation spéciale."
      }
   },
   "/appendices/employerclasses/0": {
     "self": "/appendices/employerclasses/0",
     "value": "0",
     "description": {
      "nl": "Algemene categorie voor werkgevers van commerciële of niet-commerciële aard.",
      "fr": "Catégorie générale pour les employeurs, de type commercial ou non-commercial."
     }
   }
 }
}
JSON data types
AppendixCodesResponse:
  description: A collection of appendix codes
  type: object
  properties:
    items:
      type: array
      items:
        $ref: '#/definitions/AppendixCodeLink'
    total:
      type: integer
    embedded:
      type: object
      additionalProperties:
        $ref: 'appendixCode.yaml#/definitions/AppendixCode'
AppendixCodeLink:
  allOf:
  - $ref: 'common/v1/common-v1.yaml#/definitions/HttpLink'
  - type: object
    properties:
      value:
        $ref: 'appendixCode.yaml#/definitions/AppendixCodeValue'

6.2. Filtering

A collection can be filtered using query parameters.​ You can filter on a specific resource property by specifying the property name as query param. The query-param q is reserved to implement a full text search on all the resource’s content. ​

GET

/employers

get all the employers documents in the collection

​​​Parameters

name

query-param

Filter only employers that have a specific name.

Response

body

{
  "self": "/companies?name=belg",
        "items": [{
                "href": "/companies/202239951",
                "title": "Belgacom"
        }, {
                "href": "/companies/448826918",
                "title": "Carrefour Belgium SA"
        }],
        "total": 2,
}

Response codes ​​

200

OK

Default response code, also when the filtered collection is empty ​

6.3. Pagination

Rule 15: Paging over a large collection​

Collection with too many items MUST support pagination. There are two pagination techniques:

  • offset-based pagination: numeric offset identifies a page

  • cursor-based (aka key-based or luke index): a unique key element identifies a page

Cursor-based pagination has some advantages, especially for high volumes. Take into account the considerations listed in the Zalando API guidelines before choosing a pagination technique.

Table 3. Reserved JSON properties:

next

MANDATORY (except for the last page)

hyperlink to the next page

prev

OPTIONAL

hyperlink to the previous page

pageSize

RECOMMENDED

Maximum number of items per page. For the last page, its value should be independent of the number of actually returned items.

page

MANDATORY (offset-based); N/A (cursor-based)

index of the current page of items, should be 1-based (the default and first page is 1)

first

OPTIONAL

hyperlink to the first page

last

OPTIONAL

hyperlink to the last page

Note that the total collection property, if used, MUST always present the total number of items across all pages. The names of the properties with hyperlink values and the items property are derived from the IANA registered link relations.

Table 4. Reserved query parameters:

pageSize

OPTIONAL

maximum number of items per page desired by client; must have a default value if absent.

page

MANDATORY with default value 1 (offset-based); N/A (cursor-based)

the index of page to be retrieved

Example 18. Offset-based pagination
{
  "self": "/companies?page=2&pageSize=2",
  "items": [
    {
      "href": "/companies/202239951",
      "title": "Belgacom"
    },
    {
      "href": "/companies/212165526",
      "title": "CPAS de Silly"
    }
  ],
  "pageSize": 2
  "total": 7,
  "first": "/companies?pageSize=2",
  "last": "/companies?page=4&pageSize=2",
  "prev": "/companies?page=1&pageSize=2",
  "next": "/companies?page=3&pageSize=2"
}
Example 19. Cursor-based pagination
{
  "self": "/companies?afterCompany=0244640631&pageSize=2",
  "items": [
    {
      "href": "/companies/202239951",
      "title": "Belgacom"
    },
    {
      "href": "/companies/212165526",
      "title": "CPAS de Silly"
    }
  ],
  "pageSize": 2,
  "total": 7,
  "first": "/companies?pageSize=2",
  "next": "/companies?afterCompany=0212165526&pageSize=2"
}

6.4. Create a new resource​

The collection resource can be used to create new document resources. ​

​​​​​​​​​POST

/employers

create a new employer in the collection

​​​Request

body

​The data of the resource to create.

{
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "company": {
    "cbeNbr": 202239951
  }
}

Response headers

Location

http-header

The URI of the newly created resource e.g. /employers/93017373

Response

body

The response contains an empty body.

Response codes ​​

201

Created

Default response code if the query returned results

409

Conflict

The resource could not be created because the request is in conflict with the current state of the resource. E.g. the resource already exists (duplicate key violation).

303

See Other

The resource already exists. May be returned instead of 409 Conflict if it is considered a normal use case to perform the operation for an already existing resource. The Location header refers to the resource.

POST /employers HTTP/1.1

HTTP/1.1 201 Created
Location: /employers/93017373
Content-Length: 0
Date: Wed, 06 Jan 2016 15:37:16 GMT

7. HTTP Methods

7.1. GET

GET must be used to retrieve a representation of a resource. It is a strict read-only method, which should never modify the state of the resource.

​​Status codes

​Code Description​

​200 OK

​The requested resource was found or a search request returns some content

204 No Content

The search request returns no result

​​404 Not Found

The requested resource not found

HEAD should be used to only retrieve the HTTP response headers​. HEAD returns the same response as GET, except that the API returns an empty body. It is strictly read-only.

​​Status codes

​Code Description​

​200 OK

​The requested resource was found or a search request returns some content

204 No Content

The search request returns no result

​​404 Not Found

The requested resource not found

7.3. PUT

PUT must be used to both insert and update a stored resource under a consumer supplied URI​.

If the URI refers to an already existing resource, the enclosed entity SHOULD be considered as a new version to replace the one residing on the server. If the target resource is successfully modified in accordance with the state of the enclosed representation, then a 200 (OK)​ response SHOULD be sent to indicate successful completion of the request.

If the URI does not point to an existing resource, and that URI is capable of being defined as a new resource, the server can create the resource with that URI. The server MUST inform the client by sending a 201 (Created)​ response to indicate succesful creation.

PUT operations may have side effects (i.e. modify state), but MUST be idempotent.

​​Status codes

​Code Description​

​200 OK

The existing resource is updated ​

201 Created

A new resource is created ​​

409 Conflict

A constraint on the resource is violated

7.4. POST

POST must be used to create a new resource in a collection. The POST request’s body contains the suggested state representation of the new resource to be added to the server-owned collection.

The response should contain a Location HTTP header containing the newly created URI.

POST operations may have side effects (i.e. modify state), and are not required to be idempotent.

​​Status codes

​Code Description​

​201 Created

A new resource is created

409 Conflict

The resource already exists

7.5. PATCH

PATCH must be used to update partially a representation of a resource.

The enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH.​ PATCH operations are not required to be idempotent, however they will often be in pratice.

See Partial update on how to specify the set of instructions describing how a resource on the server should be modified.

​​Status codes

​Code Description​

​200 OK

​Specific resource was requested and found or a search request returns some content

​​404 Not Found

a specific resource was requested and not found

​​409 Conflict

A constraint on the resource is violated​

Compatibility

The PATCH http method may not be supported in all components participating in the communication (e.g. HTTP client, intermediary proxy). If these can’t be upgraded to add support, client and server may agree to work around this by implementing PATCH as POST operations with HTTP header 'X-HTTP-Method-Override: PATCH'.

7.6. DELETE

DELETE must be used to remove a resource from its parent. Once a DELETE request has been processed for a given resource, the resource can no longer be found by clients. Therefore, any future attempt to retrieve the resource’s state representation, using either GET or HEAD, must result in a 404 (“Not Found”)​ status returned by the API.

​​Status codes

​Code Description​

​200 OK

Specific resource was requested and found or a search request returns some content

7.7. OPTIONS

OPTIONS should be used to retrieve metadata that describes a resource’s available interactions.

8. Status codes

The full list of HTTP status codes is documented here.

In order to conform to the REST uniform service contract, REST services should stick to this reduced list of status codes.

This HTTP status codes chart (takes a while to load) shows a decision tree to determine the usage of the correct HTTP status code.

8.1. 1xx Informational

Request received, continuing process

8.2. 2xx Success

The action was successfully received, understood, and accepted

Code Description Operations

200 OK

200 (“OK”) should be used to indicate nonspecific success

In most cases, 200 is the code the client hopes to see. It indicates that the REST API successfully carried out whatever action the client requested, and that no more specific code in the 2xx series is appropriate. Unlike the 204 status code, a 200 response should include a response body.

GET, PUT

201 Created

201 (“Created”) must be used to indicate successful resource creation

A REST API responds with the 201 status code whenever a collection creates, or a store adds, a new resource at the client’s request. There may also be times when a new resource is created as a result of some custom POST action, in which case 201 would also be an appropriate response.

PUT

202 Accepted

202 (“Accepted”) must be used to indicate successful start of an asynchronous action

A 202 response indicates that the client’s request will be handled asynchronously. This response status code tells the client that the request appears valid, but it still may have problems once it’s finally processed. A 202 response is typically used for actions that take a long while to process. A POST method may send 202 responses, but other methods should not. (TODO - long-running tasks - issue #17)

204 No Content

204 (“No Content”) should be used when the response body is intentionally empty

The 204 status code is usually sent out in response to a PUT, POST, or DELETE request, when the REST API declines to send back any status message or representation in the response message’s body. An API may also send 204 in conjunction with a GET request to indicate that the requested resource exists, but has no state representation to include in the body.

GET, PUT, POST, DELETE

8.3. 3xx Redirection

Further action must be taken in order to complete the request|

Code Description Operations

301 Moved Permanently

301 (“Moved Permanently”) should be used to relocate resources

The 301 status code indicates that the REST API’s resource model has been significantly redesigned and a new permanent URI has been assigned to the client’s requested resource. The REST API should specify the new URI in the response’s Location header.

303 See Other

303 (“See Other”) should be used to refer the client to a different URI

The 303 status code allows a REST API to send a reference to a resource without forcing the client to download its state. Instead, the client may send a GET request to the value of the Location header.

It can be used when a long-running action has finished its work, but instead of sending a potentially unwanted response body, it sends the client the URI of a response resource. This can be the URI of a temporary status message, or the URI to some already existing, more permanent, resource.

304 Not Modified

304 (“Not Modified”) should be used to preserve bandwidth

This status code is similar to 204 (“No Content”) in that the response body must be empty. The key distinction is that 204 is used when there is nothing to send in the body, whereas 304 is used when there is state information associated with a resource but the client already has the most recent version of the representation. This status code is used in conjunction with conditional HTTP requests

307 Temporary Redirect

307 (“Temporary Redirect”) should be used to tell clients to resubmit the request to another URI

A 307 response indicates that the REST API is not going to process the client’s request. Instead, the client should resubmit the request to the URI specified by the response message’s Location header.

A REST API can use this status code to assign a temporary URI to the client’s requested resource. For example, a 307 response can be used to shift a client request over to another host.

8.4. 4xx Client Error

The request contains bad syntax or cannot be fulfilled.

Code Description Operations

400 Bad Request

400 (“Bad Request”) may be used to indicate nonspecific failure 400 is the generic client-side error status, used when no other 4xx error code is appropriate.

401 Unauthorized

401 (“Unauthorized”) must be used when there is a problem with the client’s credentials.

A 401 error response indicates that the client tried to operate on a protected resource without providing the proper authorization. It may have provided the wrong credentials or none at all.

403 Forbidden

403 (“Forbidden”) should be used to forbid access regardless of authorization state.

A 403 error response indicates that the client’s request is formed correctly, but the REST API refuses to honor it. A 403 response is not a case of insufficient client credentials; that would be 401 (“Unauthorized”).

404 Not Found

404 (“Not Found”) must be used when a client’s URI cannot be mapped to a resource.

The 404 error status code indicates that the REST API can’t map the client’s URI to a resource.

405 Method Not Allowed

405 (“Method Not Allowed”) must be used when the HTTP method is not supported.

The API responds with a 405 error to indicate that the client tried to use an HTTP method that the resource does not allow. For example, when a PUT or POST action is performed on a read-only resource supporting only GET and HEAD.

A 405 response must include the Allow header, which lists the HTTP methods that the resource supports. For example: Allow: GET, POST

406 Not Acceptable

406 (“Not Acceptable”) must be used when the requested media type cannot be served

The 406 error response indicates that the API is not able to generate any of the client’s preferred media types, as indicated by the Accept request header. For example, a client request for data formatted as application/xml will receive a 406 response if the API is only willing to format data as application/json.

409 Conflict

409 (“Conflict”) should be used to indicate a violation of resource state

The 409 error response tells the client that they tried to PUT the REST API’s resources into an impossible or inconsistent state. For example, a REST API may return this response code when a client tries to DELETE a non-empty store resource.

PUT,POST,PATCH

412 Precondition Failed

412 (“Precondition Failed”) should be used to support conditional operations

The 412 error response indicates that the client specified one or more preconditions in its request headers, effectively telling the REST API to carry out its request only if certain conditions were met. A 412 response indicates that those conditions were not met, so instead of carrying out the request, the API sends this status code.

Only use for conditional HTTP requests, not constraints expressed in the HTTP payload. Use 409 Conflict instead.

415 Unsupported Media Type

415 (“Unsupported Media Type”) must be used when the media type of a request’s payload cannot be processed

The 415 error response indicates that the API is not able to process the client’s supplied media type, as indicated by the Content-Type request header. For example, a client request including data formatted as application/xml will receive a 415 response if the API is only willing to process data formatted as application/json.

8.5. 5xx Server Error

The server failed to fulfill an apparently valid request

Code Description Operations

500 Internal Server Error

500 (“Internal Server Error”) should be used to indicate API malfunction

500 is the generic REST API error response. Most web frameworks automatically respond with this response status code whenever they execute some request handler code that raises an exception.

A 500 error is never the client’s fault and therefore it is reasonable for the client to retry the exact same request that triggered this response, and hope to GET a different response.

503 Service Unavailable

503 (“Service Unavailable”) indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.

The server MAY send a Retry-After header field to suggest an appropriate amount of time for the client to wait before retrying the request.

9. Media Types

The HTTP protocol supports content negotiation for media types of the response.

Rule 16: Content negotation

REST clients SHOULD specify the media type(s) that is(/are) acceptable for the response in the Accept HTTP header.

A REST response or request with payload MUST include the Content-Type HTTP header to indicate the media type of the HTTP payload.

Status code 406 MUST be used to indicate that the requested media type could not be provided.

Status code 415 MUST be used to indicate an unsupported media type in the request payload.

If a resource supports multiple content types through negotation, the response header Vary: Accept MUST be added to avoid undesired cache hits (also see Caching).

Example 20. Content negotiation

Request HTTP headers:

Accept: image/jpeg, image/png

Response HTTP headers:

Content-Type: image/png
Vary: Accept

JSON

The primary media type to support is JSON (Javascript Object Notation RFC 7159). The choice for JSON is particularly made for supporting JavaScript clients.

Rule 17: Representation of structured data

Structured data MUST be made available in JSON format. Always stick to UTF-8 encoding and specify the charset in the Content-Type HTTP header.

Content-Type: application/json;charset=UTF-8

MIME types of format application/<subtype>+json may be used for standardized JSON formats, though REST APIs should also accept requests using the generic application/json media type:

  • application/merge-patch+json for JSON Merge Patch payloads (see Partial update)

  • application/problem+json for Problem Detail for HTTP APIs (RFC 7807) payloads (see Error handling)

XML

On specific client request, a REST service can expose XML messages defined by XML Schema.

Content-Type: application/xml;charset=UTF-8

Always stick to UTF-8 encoding and specify the charset in the Content-Type HTTP header.

It is not recommended to implement both JSON / XML in each REST service. It would require to define and implement two representations of the same data. Automatic conversion between the two standards will almost never give a satisfying result.

Media types in OpenAPI

The request and response body’s media type are specified in an OpenAPI specification respectively by the consumes and produces keywords:

  • at operation level

  • at root level, as default media types for the entire specification

Example 21. Specifying media types at operation level
  /persons/{ssin}/photo:
    get:
      summary: Get the photo of the person
      operationId: getPhoto
      produces:
      - image/jpeg  # for photo
      - application/problem+json # for problems
      parameters:
      - $ref: "person/identifier/v1beta/person-identifier-v1beta.yaml#/parameters/SsinQueryParameter"
      responses:
        200:
          description: successful operation
          schema:
            type: file
        400:
          $ref: "#/responses/InvalidSsinResponse"
        404:
          $ref: "#/responses/UnknownSsinResponse"
OpenAPI 2.0 doesn’t support specifying a different media type per status code.
Rule 18: Default media types

OpenAPI 2.0 specifications SHOULD specify following default media types:

consumes:
- application/json

produces:
- application/json
- application/problem+json

10. Performance

10.1. Embedding resources

In order to reduce the number of requests, optional embedding of subresources may be used.

Rule 19: Embedding subresources

Embedded subresources MUST be put in a JSON object named embedded at root level of the JSON document. In this object, each property key is the URI and the value the full JSON representation of the subresource. The URI MUST be the same as the href value by which the subresource is referenced in the representation of the main resource.

Embedded resources are returned at the client’s request if the embed query parameter is specified. The name of the JSON property referencing the subresource can be used as value of the embed parameter.

This way of embedding resources limits the changes required when a clients starts to use it, and only impacts the JSON Schema type of the root document. It also works well when there are multiple occurrences of the same subresource, and when embedding resources more than one level deep.

In JSON Schema, additionalProperties can be used to specify the data types allowed in embedded. Note that it not possible to specify more than one data type this way in OpenAPI 2.0, so the only restriction that can be specified is type: object when there are multiple embedded data types. In OpenAPI 3.0, oneOf can be used to list all possible data types.

Example 22. Embedding subresources
{
  "self": "http://myrestapi/family/32456",
  "family": {
    "parents": [{
      "ssin": "12345678901",
      "href": "http://myrestapi/people/12345678901"
    }],
    "children": [{
      "ssin": "98765432101",
      "href": "http://myrestapi/people/98765432101"
    }]
  },
  "embedded": {
    "http://myrestapi/people/12345678901": {
      "self": "http://myrestapi/people/12345678901",
      "ssin": "12345678901",
      "givenName": "Jane",
      "lastName": "Doe"
    },
    "http://myrestapi/people/98765432101": {
      "self": "http://myrestapi/people/98765432101",
      "ssin": "98765432101",
      "givenName": "Bobby",
      "lastName": "Doe"
    }
  }
}
JSON data type
Family:
  description: A family
  type: object
  properties:
    parents:
      type: array
      items:
        $ref: '#/definitions/PersonRef'
    children:
      type: array
      items:
        $ref: '#/definitions/PersonRef'
    embedded:
      type: object
      additionalProperties:
        $ref: 'person.yaml#/definitions/Person'
Example 23. Query parameters for subresources more than one level deep

10.2. Caching

Rule 20: HTTP Caching

HTTP caching SHOULD be considered for resource types for which the same resources are repeatedly retrieved by clients.

The following schema shows a decision tree for the caching setup for a given resource:

http cache decision tree

10.2.1. Conditional requests

Conditional requests are those where the client can ask the server if it has an updated copy of the resource. The client will send some information about the cached resource it holds and the server will determine whether updated content should be returned or the client’s copy is already up to date. In the latter case, an HTTP status of 304 Not Modified is returned.

conditional

Though conditional requests do invoke a call across the network, unmodified resources result in an empty response body – saving the cost of transferring the resource back to the end client. The backend service is also often able to very quickly determine a resource’s last version or modification date without accessing the resource which itself saves non-trivial processing time.

Time-based

A time-based conditional request is based on the the last modified time of the resource. If the cached copy’s last modification time is still the most recent one, the server returns the 304 response code. To enable time-based conditional requests, the application specifies the last modified time of a resource via the Last-Modified response header.

Last-Modified: Mon, 03 Jan 2011 17:45:57 GMT

The next time the browser requests this resource it will only ask for the contents of the resource if they’re unchanged since this date using the If-Modified-Since request header

If-Modified-Since: Mon, 03 Jan 2011 17:45:57 GMT

If the resource hasn’t changed since Mon, 03 Jan 2011 17:45:57 GMT the server will return with an empty body with the 304 response code.

Rule 21: Precision of Last-Modified header

The Last-Modified header doesn’t allow subsecond precision, which may lead to incorrect cache hits if there are two updates within a second.

Because of this, the ETag SHOULD be preferred over the Last-Modified header.

Content-based

The ETag (or Entity Tag) works in a similar way as the Last-Modified header except its value is a free-form tag identifying a resource version. Valid ETags can be a MD5 hash, a version number or a modification timestamp, allowing more precision than when using a Last-Modified-header.

This allows the server to identify if the cached contents of the resource are different to the most recent version.

ETag: "15f0fff99ed5aae4edffdd6496d7131f"

On subsequent browser requests, the If-None-Match request header is sent with the ETag value of the last requested version of the resource.

If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"

As with the If-Modified-Since header, if the current version has the same ETag value as the browser’s cached copy, then an HTTP status of 304 is returned.

10.2.2. Client caching directives

Cache-Control header

An HTTP client cache may cache server responses and decide to not even contact the server when the resource is requested again, saving the round trip to the server. The Cache-Control response header specifies directives for the client under which conditions and how long it should cache the response contents. This is useful for resources which don’t change frequently, and a client doesn’t need to be always synchronized with the latest version of the resource.

Example 24. Cache-Control response header
Cache-Control:public, max-age=86400

The response data may be cached by clients and intermediary servers as it is public, and should expire from the cache after 1 day (86400 seconds).

Vary header

The Vary response header describes which request headers, aside from the method, Host header field, and request target, influence the origin server’s process for selecting and representing this response. It is used to prevent unwanted cache hits.

See Best Practices for Using the Vary Header for more guidelines on the usage of the Vary header.

Example 25. Vary response header
Vary: Accept

This avoids using a cached XML response when a second request asks for JSON.

Never use Vary: * as it will result in a cache hit of 0.

11. JSON

All RESTful services must support JSON (Javascript Object Notation RFC 7159).

11.1. JSON object

For extensibility, the payload of a RESTful request or response should always be a JSON object (instead of an array, string , number, etc). This way the request or response can always be extended with new properties.

Service consumers should be aware that JSON objects can always be extended with additional JSON properties. This shouldn’t break the client code. The unknown properties must be ignored.

11.2. JSON properties

Rule 22: Naming of JSON properties

All JSON property names are in American English and are written in lowerCamelCase notation. For abbreviations as well, all letters except the first one should be lowercased. Do not use underscore or hyphens for composite words.

Properties from other standards, like OpenID Connect and OAuth2 are an exception to this rule.

Overly generic terms like info(rmation) and data SHOULD NOT be used as property name or as part of it.

A JSON property SHOULD refer to the business meaning of its value rather than how it is defined.

KO OK

customerInformation

customer

descriptionString

description

{
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "enterprise": {
    "enterpriseNumber": "0202239951"
  }
}
Rule 23: Null values

Properties with a null value SHOULD be stripped from the JSON message.

A notable exception to this rule are JSON Merge Patch requests, in which a null value indicates the removal of a JSON property.

Note that this rule doesn’t apply to empty values (e.g. empty strings "" or empty arrays []) as they are considered different from a null value.

NOK

OK

{
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "enterprise": null
}
{
  "name": "Belgacom",
  "nssoNbr": 93017373
}

The JSON properties have no specific order inside a JSON object.

{
  "name": "Belgacom",
  "nssoNbr": 93017373
}
{
  "nssoNbr": 93017373,
  "name": "Belgacom"
}

Dates are written in ISO 8601 full-date format: yyyy-mm-dd

{
  "birthday": "1930-07-19"
}

Date/time are written in ISO 8601 date-time format: yyyy-MM-dd’T’HH:mm:ssXXX

Example UTC
{
  "lastModification": "2016-04-24T09:26:01.5214Z"
}
Example with timezone
{
  "lastModification": "2016-04-24T11:26:00+02:00"
}

12. API specifications

Rule 24: API contract-first approach

The contract-first principle SHOULD be followed when developing an API. In this approach, the specifications of the REST API are created first and not generated from the code.

A code-first approach may sometimes be necessary however, as current state of technology does not always fully support contract-first development. In this case, special attention should be given that the API specifications remain stable and loosely coupled to the API implementation technology.

12.1. OpenAPI (Swagger)

OpenAPI is the dominating standard for API documentation, having gained wide industry support. It is based on Swagger, which has been standardized as OpenAPI 2.0.

Rule 25: OpenAPI 2.0 (Swagger)

Specifications of the API SHOULD be provided using OpenAPI 2.0 (aka Swagger). OpenAPI 2.0 uses the OpenAPI Schema Object to describe the JSON representation of resources, which is a variant of the JSON Schema draft 04 standard.

The newer OpenAPI 3.0 standard should not be used yet for now, as tooling and library support is not yet sufficiently up to date with this version. You can visualize OpenAPI files using tools like Swagger UI, Swagger UI Watcher or Senya.

Rule 26: Additional documentation

Any additional documentation SHOULD be linked from the API’s OpenAPI definition using the externalDocs property.

Rule 27: Use YAML format for OpenAPI

OpenAPI files SHOULD be made available in YAML format, and OPTIONALLY in JSON format as well.

YAML is a superset of JSON which allows a simpler notation, and also allows comments. According to usage, a conversion step to JSON might be necessary considering limitations of tools.

Rule 28: Use tags to group operations

When an API has many operations, use tags to group them together. This will make the visual representation (SwaggerUI) more readable.

Rule 29: doc and refData resources

The OpenAPI specification file and other API documentation SHOULD be placed under a special doc resource under the API root location.

The swagger specification file is, by convention, named swagger.json or swagger.yaml. Root OpenAPI 3.0 documents SHOULD be named openapi.json or openapi.yaml, as specified in the OpenAPI 3.0 specification.

Resources representing reference data (code lists) specific to an API SHOULD be placed under a refData resource. As reference data is typically static, consider supporting Caching headers for these resources.

Note that these are exceptions to the rule that API resource URLs shouldn’t have a file extension.

Example 26. doc and refData resources
 /doc
     /swagger.json
     /swagger.yaml
     /<optional other documentation>
 /refData
     /<list1OfCodes>
        /<code1>
        /<code2>
        /...
     /<list2OfCodes>
        /...
 /<resource1>
     /...
 /<resource2>
     /...
 ...
Rule 30: Add examples in OpenAPI

Add example response values to the OpenAPI specification under the examples property.

  /enterprises/{enterpriseNumber}:
    get:
      operationId: getEnterprise
      parameters:
      - in: path
        name: enterpriseNumber
        required: true
        type: string
      responses:
        200:
          description: successful operation
          schema:
            $ref: '#/definitions/Enterprise'
          examples:
            application/json:
              {
                "name": "Belgacom",
                "enterpriseNumber": "0202239951"
              }
OpenAPI 2.0 only allows a single example per media type under examples. Any additional examples should be put in external documentation.
Rule 31: Reusable OpenAPI definitions

Instead of specifying everything directly in the swagger.yaml file of an API, OpenAPI allows to reference data types and other definitions from other reusable files. These files SHOULD follow the Swagger/OpenAPI file format as well and may include data type definitions, but also parameter, path items and response objects.

To work around limitations of certain tools, a conversion step to inline the definitions into the swagger.yaml file may be necessary.

Duplication of types in multiple APIs SHOULD be avoided. Rather, put the type in a reusable OpenAPI file. Files reusable from multiple APIs SHOULD be organized in this structure:

<domain>/<version>/<domain-version>.yaml
<domain>/<subdomain>/<version>/<domain-subdomain-version>.yaml

Definitions SHOULD be grouped per (sub)domain in a file. Each file has its own lifecycle, with a major version number in it’s directory and file name, that is increased when backwards compatibility is broken. This version, with optionally a minor and patch version added to it, MUST be specified in the info section in the swagger file as well.

While it is not strictly necessary for external definitions to be put in a valid OpenAPI 2.0 file, doing so makes it possible to use standard OpenAPI/Swagger tooling on them.

Example 27. Reusable OpenAPI file
/person/identifier/v1/person-identifier-v1.yaml
swagger: "2.0"
info:
  title: data types for person identifiers
  version: v1beta
paths: {} # empty paths property required to be a valid OpenAPI file
definitions:
  Ssin:
    description: "Social Security Identification Number issued by the National Register or CBSS"
    type: string
    pattern: \d{11}

A type can be referenced from another OpenAPI file:

"$ref": "./person/identifier/v1/person-identifier-v1.yaml#/definitions/Ssin"
Rule 32: Common definitions for Belgian government institutions

Common definitions for use by Belgian government institutions are published on https://www.gcloud.belgium.be/rest/schemas/ (TODO: this path is not yet navigable). Types in these schemas SHOULD be used instead of defining your own variants.

The technical types referenced in this style guide are available in the common and problem subpath. Other types for business concepts commonly used by Belgian government institutions are added in other subpaths.

The OpenAPI files will be made available both in YAML and JSON format through content negotiation (see Media Types), with YAML being the default format. (TODO: only available as <type>.yaml currently)

12.2. JSON data types

Rule 33: Naming of data types

Data type names SHOULD be defined in American English and use UpperCamelCase notation. For abbreviations as well, all letters except the first one should be lowercased.

Overly generic terms like info(rmation) and data SHOULD NOT be used as data type name or part of it.

A data type name SHOULD refer to the business meaning rather than how it is defined.

KO OK

CustomerInformation

Customer

LanguageEnumeration

Language

Rule 34: Data type description

The description property MAY provide a textual description of a JSON data type. The title property MUST NOT be used because it is hides the actual data type name in visualization tools like Swagger UI.

KO OK
Pet:
  title: a pet in the pet store
  type: object
Pet:
  description: a pet in the pet store
  type: object

additionalProperties can be used to put restrictions on other properties of a JSON object than those specified in the schema.

Rule 35: additionalProperties

additionalProperties SHOULD be used exclusively to describe an object representing a map of key-value pairs. The keys of such maps don’t need to respect the naming rules for JSON properties (lowerCamelCase and English).

An example is the description a map of embedded resources, as described in [Embedded resources]. Other uses of additionalProperties than for maps are to be avoided, in order to support schema evolution.

Rule 36: readOnly properties

Properties SHOULD be declared readOnly when appropriate.

Properties can be declared readOnly: true. This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request. Properties marked as readOnly being true SHOULD NOT be in the required list of the defined schema.

Examples are properties that are computed from other properties, or that represent a volatile state of a resource.

Rule 37: Enum values

A fixed list of possible values of a property can be specified using enum. However, this may make it harder to change the list of possible values, as client applications will often depend on the specified list e.g. by using code generation.

enum SHOULD only be used when the list of values is unlikely to change or when changing it has a big impact on clients of the API.

Enumerated string values SHOULD be declared in lowerCamelCase, just as property names.

Example 28. Enum declaration
state:
  type: string
  enum:
  - processing
  - failed
  - done
Rule 38: String and integer types

When defining the type for a property representing a numerical code or identifier:

  • if the values constitute a list of sequentially generated codes (e.g. gender ISO code), type: integer SHOULD be used. It is RECOMMENDED to further restrict the format of the type (e.g. format: int32).

  • if the values are of fixed length or not sequentially generated, type: string SHOULD be used (e.g. Ssin, EnterpriseNumber). This avoids leading zeros to be hidden.

When using a string data type, each code SHOULD have a unique representation, e.g. don’t allow representations both with and without a leading zeros or spaces for a single code. If possible, specify a pattern with a regular expression restricting the allowed representations.

12.3. Tools

Following tools can be used to edit OpenAPI files

Name Link Description

KaiZen OpenAPI editor

https://github.com/RepreZen/KaiZen-OpenAPI-Editor

Open Source Eclipse plugin. Text only editor.

Swagger UI

https://swagger.io/swagger-ui/

Browser application. Graphical and text view of Swagger files. Does not support references to external files.

Swagger UI watcher

https://github.com/moon0326/swagger-ui-watcher

Swagger UI with multi-file support. Only supports viewing. View is refreshed on each file change.

Zalando’s Swagger plugin

https://github.com/zalando/intellij-swagger

Plugin for IntelliJ. Text-only Swagger editor

RepreZen

https://www.reprezen.com/

Commercial editor based on KaiZen. Graphical and text view.

Senya Editor

https://senya.io

Commercial IntelliJ IDEA plugin. Graphical and text view.

Following tools can be used to generate server stubs and API client libraries from OpenAPI specification files.

Name Link Comments

openapi-generator

https://openapi-generator.tech/

Started as fork of swagger-codegen.

swagger-codegen

https://github.com/swagger-api/swagger-codegen

swagger-validator-maven-plugin

https://github.com/sylvainlaurent/swagger-validator-maven-plugin

Perform validation of swagger files during build.

styleguide-validationrules

https://git-cicd.gcloud.belgium.be/rest/styleguide-validationrules (network access restricted)

Validation rules for swagger-validator-maven-plugin to check conformance to this style guide.

13. Hypermedia controls

Thanks to hypermedia controls a client can navigate through the REST API without hardcoding URIs to specific resources. It suffices to know a limited set of entry points and navigate to the desired information. This adds flexibility to the evolution of the API and increases loose coupling between client and server.

Rule 39: Links to self and other resources

When referencing another resource, a link SHOULD be added using the href attribute. Each resource SHOULD also contain its own location in a self attribute at root level of the JSON document. URIs SHOULD always be absolute.

For brevity, most URIs in the style guide examples are shortened, but in reality URIs should always be absolute.

All href hyperlinks are derived from this minimal link type:

HttpLink JSON data type (from common-v1.yaml)
HttpLink:
  description: A base type of objects representing links to resources.
  type: object
  properties:
  href:
    description: Any absolute URI that is using http or https protocol
    type: string
    format: uri
    readOnly: true

self hyperlinks are derived from the following type:

SelfLink JSON data type (from common-v1.yaml)
SelfLink:
  description: A base type representing a link to the resource's own location within its representation
  type: object
  properties:
    self:
      description: Absolute URI (http or https) to the the resource's own location.
      type: string
      format: uri
      readOnly: true

Links should not be used in request bodies in a PUT, POST or PATCH context, as indicated by the readOnly: true property. In a JSON response, they can be added anywhere.

Example 29. Resource links
{
   "self": "/companies/1"
   "owner": {
      "ssin": "12345678901",
      "href": "http://example.org/v1/people/12345678901"
   },
   "website": "https://wwww.mycompany.com"
}
{
   "owner": {
      "ssin": "12345678902"
   },
   "website": "https://wwww.mynewwebsite.com"
}

The corresponding JSON data type includes the link types using allOf. As website isn’t a reference to another API resource, it is not defined using the HttpLink type.

definitions:
  Company:
    allOf:
    - "$ref": http://www.gcloud.belgium.be/rest/schemas/common/v1/common-v1.yaml#/definitions/SelfLink
    - type: object
      properties:
        owner:
          "$ref": "definitions/PersonReference"
        website:
          type: string
          format: uri
  PersonReference:
    allOf:
      - "$ref": http://www.gcloud.belgium.be/rest/schemas/common/v1/common-v1.yaml/definitions/HttpLink
      - type: object
        properties:
          ssin:
            "$ref": "#definitions/Ssin"

Hyperlinks 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 link type.

The use of Web Linking and Hypertext Application Language (HAL) is not recommended.

14. Reserved words

A list of reserved words exists for common used practices

14.1. Query parameters

Term Description Example Ref

page

When a collection resources is paged, use this parameter to request a specific page. Page numbers are 1-based.

?page=3&pageSize=20

Pagination

pageSize

When a collection resources is paged, use this parameter to specify the page size.

?page=3&pageSize=20

Pagination

q

The standard search parameter to do a full-text search.

?q=Belgacom

Filtering

select

Filter the resource properties to the ones specified.

?select=name&select=address

Consult (Document)

sort

Multi-value query param with list of properties to sort on. Default sorting direction is ascending. To indicate descending direction, the property may be prefixed with -.

?sort=-name

Collection

embed

Request to embed subresource

?embed=mainAddress

Embedding resources

lang

language to filter multi-language descriptions

?lang=fr

Multi-language descriptions

14.2. JSON properties

Term Description Example Ref

next

The next-reference contains the absolute URL of the next page in a paged collection.

Links

prev

The previous-reference contains the absolute URL of the previous page in a paged collection.

Links

self

The self-reference contains the absolute URL of the resource itself.

Links

href

A reference (absolute URL) to another resource.

Links

first

A reference (absolute URL) to the first page in a paged collection.

Pagination

last

A reference (absolute URL) to the last page in a paged collection.

Pagination

items

an array with the items of a collection resource

Collection

total

the total number of items in a collection resource

Collection

page

the index of a page in a paged collection

Pagination

pageSize

the maximum number of items in a page of a paged collection

Pagination

embedded

a map of embedded subresources, with URIs as property key and the resource as value

Embedding resources

14.3. HTTP headers

This list includes all standardized and common non-standard HTTP headers.

Rule 40: Custom HTTP headers

Custom HTTP headers SHOULD be prefixed with the organization’s name.

Custom headers that are standardized across Belgian Government organizations use the BelGov- prefix.

X- headers were initially reserved for unstandardized parameters, but the usage of X- headers is deprecated (RFC-6648). Instead, it is recommended that company specific header' names should incorporate the organization’s name. However, for backwards compatibility reasons, headers with the X- prefix may still be used.

Table 5. Standard HTTP Headers referenced in the style guide
HTTP Header Type Description Reference

Location

Response

Used in redirection, or when a new resource has been created.

Create a new resource​, POST, status codes 301, 303 and 307

Accept

Request

Media type(s) that is(/are) acceptable for the response.

Media Types

Content-Type

Request/Response

The Media type of the body of the request.

Media Types

X-HTTP-Method-Override

Request

Override the method specified in the request.

PATCH

Retry-After

Response

Suggest amount of time for the client to wait before retrying the request when temporarily unavailable

[http-503]

Allow

Response

Valid methods for a specified resource.

[http-405]

ETag

Request

Identifier for returned response content

Conditional requests

If-None-Match

Response

Return resource if ETag changed

Conditional requests

Last-Modified

Request

Time on which resource was last modified

Conditional requests

If-Modified-Since

Response

Return resource only if changed since specified timestamp

Conditional requests

Vary

Response

Specifies which request headers change response content

Client caching directives

Cache-Control

Response

Indicates HTTP client how to cache responses

Client caching directives

Table 6. BelGov HTTP headers
HTTP Header Type Description Reference

BelGov-Trace-Id

Request/Response

Unique ID for tracing purposes, identifying the request or response

Tracing

BelGov-Related-Trace-Id

Response

BelGov-Trace-Id value used on related request

Tracing

14.4. Resource names

path Description Reference

/doc, /doc/swagger.yaml, /doc/swagger.json

API documentation (swagger file and other)

doc resource

/refData

resources representing reference data used in the API (i.e. code lists)

doc resource

/health

API health status

monitoring (TODO - WIP)

15. Error handling

Problem Details for HTTP APIs (RFC 7807) defines a simple JSON format to convey additional information about an error to an API consumer. This information complements the HTTP status codes [RFC7231].

For any problem occurred during processing, whether it be caused by the client or the server, the API provider supplies

  1. a suitable status code: 4xx for consumer or 5xx for server errors

  2. a RFC 7807 problem detail message

Problem Details JSON schema (from problem-v1.yaml)
Problem:
  description: A Problem Details object (RFC 7807)
  type: object
  properties:
    type:
      type: string
      format: uri
      description: An URI reference that identifies the problem type. When dereferenced, it SHOULD provide human-readable documentation for the problem type (e.g. using HTML).
      default: about:blank
    title:
      type: string
      description: A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized)
      example: Service Unavailable
    status:
      type: integer
      format: int32
      description: The HTTP status code generated by the origin server for this occurrence of the problem.
      minimum: 400
      maximum: 600
      exclusiveMaximum: true
      example: 503
    detail:
      type: string
      description: A human-readable explanation specific to this occurrence of the problem
    instance:
      type: string
      format: uri
      description: A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
Rule 41: Media type

RFC 7807 defines the media type application/problem+json

Rule 42: Unique error id

Each status message contains a unique id to track the message and store it for support use later on.

HTTP/1.1 404 Not Found
Server: Apache-Coyote/1.1
Content-Type: application/problem+json
X-Correlation-Id: d9e35127-e9b1-4201-a211-2b52e52508df
{
  "type": "https://www.gcloud.belgium.be/rest/problems/resourceNotFound"
  "instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
  "status": 404,
  "title": "Enterprise not found",
  "detail": "No enterprise with CBE number 0206731645",
  "invalidParams": [
      {
        "in": "path",
        "name": "enterpriseNumber",
        "reason": "the enterprise number is not assigned",
        "value": "0206731645"
      }
  ]
}
Rule 43: Consumer-friendly details

Each status message contains a detail property to provide specific information about the failing requests to the consumer.

POST /enterprises/0203456798/employers
{
    "name": "John"
}
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
   "instance": "88d8393b-f314-4cb3-8ef8-047971dba4ba",
   "type": "/problems/enterpriseInactive",
   "title": "Conflict",
   "status": 409,
   "detail": "Enterprise 0203456798 has ceased its activities since 2017-01-01"
}
Rule 44: Error sanitization

A problem cannot leak sensitive implementation and infrastructure details. Providers must strip this information (e.g. DB error codes, internal hostnames, stacktraces) before returning the problem detail to the consumer.

E.g. this should not be returned to a client:

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: application/problem+json
{
  "instance": "ac19acc6-5e11-4b2a-8c10-f9680998d07a",
  "title": "Internal Server Error",
  "detail": "Unexpected error occurred while processing demand",
  "stackTrace": [  [
      "EJBException: java.lang.RuntimeException: Something horrible happened on the server",
      "org.jboss.as.ejb3.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:191)",
      "org.jboss.as.ejb3.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:282)",
      "org.jboss.as.ejb3.CMTTxInterceptor.required(CMTTxInterceptor.java:345)",
      "org.jboss.as.ejb3.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:243)"
    ],
    [
      "Caused by: java.lang.RuntimeException: Something horrible has happened on the server",
      "sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
      "sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)",
      "sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
      "java.lang.reflect.Method.invoke(Method.java:606)"
    ]
  ]
}

15.1. Standardized problem types

A list of problem types is standardized for all G-Cloud REST APIs. The problem description has a type defined in https://www.gcloud.belgium.be/rest/problems.

Links to https://www.gcloud.belgium.be/rest/problems are not resolvable at the moment. This will be fixed in the future.

15.1.1. Bad Request

Status code 400 Bad Request

Description The consumer request is not conform to the Swagger specification of the API. If the parameters are invalid, look for more details in the invalidParams property.

POST /enterprises/abc

{
    "mandator": {
        "enterpriseNumber": "123456"
    }
}

returns

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/badRequest",
   "status": 400,
   "title": "Bad Request",
   "detail": "The input message is incorrect",
   "invalidParams": [
       {
          "in": "path",
          "name": "enterpriseNumber",
          "reason": "should be numeric",
          "value": "abc"
       },
       {
          "in": "body",
          "name": "mandator.enterpriseNumber",
          "reason": "should have 10 digits",
          "value": "123456"
       }
   ]
}
InvalidParamProblem schema definition (from problem-v1.yaml)
InvalidParamProblem:
  description: Problem details for invalid input parameter(s)
  allOf:
    - $ref: '#/definitions/Problem'
    - type: object
      properties:
        invalidParams:
          type: array
          description: An array of parameter OpenAPI violations
          items:
            $ref: '#/definitions/InvalidParam'
InvalidParam:
  type: object
  properties:
    in:
      description: The location of the invalid parameter (cfr Swagger parameters)
      type: string
      enum:
        - body
        - path
        - query
        - header
    name:
      description: The name of the invalid parameter
      type: string
    reason:
      description: A message explaining the violation
      type: string
    value:
      description: The value of the erroneous parameter
      # no type specified, allowing any type. This is valid OpenAPI 2.0 even though some editors may indicate an error (issue #25)

15.1.2. No Access Token

Status code 401 Unauthorized

Description The consumer must pass a valid access token in the Authorization HTTP header for each request to a secure resource.

POST /enterprises
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245

If no access token was found, the service returns:

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/noAccessToken",
   "status": 401,
   "title": "No Access Token",
   "detail": "No Bearer access token found in Authorization HTTP header"
}

15.1.3. Invalid Access Token

Status code 401 Unauthorized

Description The consumer must pass a valid access token in the Authorization HTTP header for each request to a secure resource.

POST /enterprises
Authorization: Bearer foo
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/invalidAccessToken",
   "status": 401,
   "title": "Invalid Access Token",
   "detail": "The Bearer access token found in the Authorization HTTP header is invalid"
}

15.1.4. Expired Access Token

Status code 401 Unauthorized

Description The access token passed in the Authorization HTTP header has expired and cannot be used anymore. Renew the access token and resubmit the request.

POST /enterprises
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/expiredAccessToken",
   "status": 401,
   "title": "Expired Access Token",
   "detail": "The Bearer access token found in the Authorization HTTP header has expired"
}

15.1.5. Missing Scope

Status code 403 Forbidden

Description The consumer access token doesn’t have the required scope to invoke the operation. The requiredScopes property lists the required scopes.

GET /enterprises/202239951
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/missingScope",
   "status": 403,
   "title": "Missing Scope",
   "detail": "Forbidden to consult the enterprise resource",
   "requiredScopes": ["enterprise-read"]
}

15.1.6. Missing Permission

Status code 403 Forbidden

Description The consumer doesn’t have the right to invoke an operation on the given resource. The refusal is not related to the consumer scope but to access to the specific resource itself (data access).

PUT /enterprises/202239951
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
   "type": "https://www.gcloud.belgium.be/rest/problems/missingPermission",
   "status": 403,
   "title": "Missing Permission",
   "detail": "Not permitted to update the details of this enterprise"
}

15.1.7. Resource Not Found

Status code 404 Not Found

Description The requested resource cannot be found. The detail property reveals additional information about why the resource was not found.

Either the resource path isn’t specified in the API’s /doc/swagger.yaml specification.

GET /enterprises

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "https://www.gcloud.belgium.be/rest/problems/resourceNotFound"
  "status": 404,
  "title": "Resource Not Found",
  "detail": "No resource /enterprises found"
}

Either the path parameters don’t resolve to an existing resource. Look for the more details in the invalidParams property.

GET /enterprises/0206731645

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "https://www.gcloud.belgium.be/rest/problems/resourceNotFound"
  "status": 404,
  "title": "Enterprise not found",
  "detail": "No enterprise with CBE number 0206731645",
  "invalidParams": [
      {
        "in": "path",
        "name": "cbeNumber",
        "reason": "the CBE number is not assigned",
        "value": "0206731645"
      }
  ]
}

15.1.8. Too Many Requests

Status code 429 Too Many Requests

Description The consumer quota was passed. The property limit contains the number of requests allowed. retryAfter indicates the time when the counters will be reset. retryAfterSec indicates how many seconds the consumer must wait before resubmitting the request.

GET /enterprises/0206731645
HTTP/1.1 429 Too many requests
Content-Type: application/problem+json
{
   "type": "https://www.gcloud.belgium.be/rest/problems/tooManyRequests",
   "status": 429,
   "title": "Too Many Requests",
   "detail": "No more requests accepted before 2018-08-09T06:56:00Z",
   "limit": 200,
   "retryAfter": "2018-08-09T06:56:00Z",
   "retryAfterSec": 60
}

15.2. API specific problem types

Each API can define its own problem types. The path /problems/{problemId} should resolve to a HTML page containing additional information about the specific problem.

POST /enterprises/0203456798/employers
{
    "name": "John"
}
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
   "instance": "88d8393b-f314-4cb3-8ef8-047971dba4ba",
   "type": "/problems/enterpriseInactive",
   "title": "Conflict",
   "status": 409,
   "detail": "Enterprise 0203456798 has ceased its activities since 2017-01-01"
}

16. Versioning

16.1. Preserving compatibility

Don’t Break Backward Compatibility

Change APIs, but keep all consumers running. Consumers usually have independent release lifecycles, focus on stability, and avoid changes that do not provide additional value. APIs are service contracts that cannot be broken via unilateral decisions.

There are two techniques to change APIs without breaking them:

  • evolve the API, allowing clients to gradually implement changes by following rules for compatible extensions and deprecation

  • introduce new major API version and still support older version(s) for a period in which clients can transition to the new version

Backwards-incompatible changes, requiring a big bang release, are only feasible if:

  • there is a very limited number of clients

  • the release is well coordinated

  • a temporary service interruption is allowed.

In this case, the major version number could be kept the same as only a single version of the API is active at a given time and it can be easier to keep the base URL of the API unchanged.

Rule 45: Prefer API evolution

We strongly encourage using API evolution when possible and discourage versioning:

  • versioning requires maintaining and supporting two service contracts

  • API evolution maintains stable URIs (e.g. links from other APIs) and allows for a more gradual migration of clients

Use of media type versioning (using Accept/Content-Type HTTP headers) isn’t included as an option in this guide as it has various practical problems

16.2. API evolution

Rule 46: JSON object as top level structure

In a response body, you MUST always return a JSON objects (and not e.g. an array) as a top level data structure to support future extensibility.

JSON objects support compatible extension by additional attributes. This allows you to easily extend your response and e.g. add pagination later, without breaking backwards compatibility.

With Postel’s Law in mind, here are some rules for providers and consumers that allow us to make compatible changes without versioning:

Rule 47: Compatible extensions

Apply the following rules to evolve RESTful APIs in a backward-compatible way:

  • New fields may be added to response. Clients MUST ignore unknown fields in response payloads.

  • Add only optional, never mandatory fields to request payloads

  • Never change the meaning of a field.

  • Never change input validation logic to be more restrictive

  • Enum ranges can be reduced when used as input parameters, only if the server is ready to accept and handle old range values too.

  • Enum values can be reduced when used as output parameters, but not extended as clients might not be ready to handle them.

  • Support redirection in case an URL has to change (301 Moved Permanently)

  • a new resource variant (with another name) can be created in addition to the old resource variant

Rule 48: Deprecation

Deprecation MAY be used to indicate parts of an API are not recommended to be used anymore.

OpenAPI 2.0 only allows specifying deprecation on methods and path elements. Their deprecation SHOULD be documented in OpenAPI by specifying deprecated: true. For other deprecations e.g. of a JSON property, the custom OpenAPI extension x-deprecated: true SHOULD be used.

Deprecated elements MAY be completely removed them after a period in which clients can adapt. Always communicate clearly about the deprecated fields and transition period to clients.

Use of deprecated elements in requests may be monitored by the server in order to evaluate the impact of their removal. However, this isn’t possible for response elements, so there is always remains some risk for breaking clients.

16.3. API versioning

Rule 49: Version numbering

The version string of an API SHOULD contain a major, minor and optionally a patch number.

Only the major version of the API is part of the base URL (basePath in Swagger) of the API. Thereby, for a major version only a single minor and patch version can be available in production at a time. The version of the API MUST be included in the info section of the OpenAPI definition.

Note that reusable OpenAPI files are to be versioned as well, as specified in OpenAPI (Swagger).

Example 31. API version in OpenAPI 2.0
swagger: 2.0
info:
  name: petshop
  description: API exposing my petshop’s functionality
  version: 2.1.2
host: petstore.swagger.io
basePath: /v2

Similar guidelines apply to sunset an old major version of an API apply as for removal of deprecated elements.

Rule 50: Removing an old version

Before removing an old API version:

  • provide a transition period supporting old and new versions at the same time before removing the old version

  • always communicate clearly about the transition period to clients

  • use of the old version may be monitored by the server in order to evaluate the impact of their removal.

17. Internationalization (I18N)

Textual descriptions part of a resource may need to be offered in the language of a user.

One of following strategies may be chosen for internationalization:

  • Do not offer textual descriptions in the API response. Offer them only separate from the data, for example as a dedicated collection resource in the API returning a code list with descriptions. This list can be cached by a client.

  • Only include descriptions in a single language, specified by the caller in the Accept-Language header.

  • Offer descriptions in all applicable languages, i.e. for APIs offered by the Belgian Government in French, Dutch, German and sometimes English as well. This approach is not scalable to high number of languages. The descriptions may be filtered by a lang query parameter.

17.1. Accept-Language

As for Media Types, HTTP supports content negotiation for the language used in response content. ``

Rule 51: HTTP language headers

Users can inform the server that a specific language is requested by adding the Accept-Language HTTP header to each request (RFC 7231).

Its value is a language tag (RFC 5646), consisting of a ISO 639-1 lowercase language code, optionally followed by an uppercase country code indicating a country-specific variant. Internationalized APIs of Belgian Federal Government organizations MUST also support the variants for the official Belgian languages ending on -BE. Multiple languages may be specified in order of preference. A wildcard * matches any language.

The Content-Language response header MUST be used to indicate the language used in the response for internationalized content.

In case the server could not honor any of the requested languages, it SHOULD return a 406 Not Acceptable error. If the resource supports caching, the Vary: Accept-Language MUST be included in the response.

Example 32. HTTP language headers
Request headers
Accept-Language: nl-BE, nl, fr-BE, fr, en, *
Response headers
Content-Language: nl

17.2. Multi-language descriptions

It may be decided to include all supported translations for descriptions in a resource’s representation.

Rule 52: Multi-language descriptions

A multi-language description SHOULD be represented by a JSON object with ISO 639-1 language codes as property names and the corresponding textual description as values.

An API MAY offer to filter the representation to a single language by using the reserved query parameter lang.

A LocalizedString type is defined supporting the three official Belgian languages.

LocalizedString JSON Schema (from common-v1.yaml)
LocalizedString:
  description: A description specified in multiple languages
  type: object
  properties:
  fr:
    type: string
  nl:
    type: string
  de:
    type: string
Example 33. Multi-language descriptions
Request

GET /countries/BE

Response
"name": {
 "fr": "Belgique",
 "nl": "België",
 "de": "Belgien"
}
Filtering a single language

GET /countries/BE?lang=fr

"name": {
 "fr": "Belgique"
}

18. Tracing

In order to uniquely identify requests or response messages, a tracing ID may be added as HTTP header. This tracing id can be used to be able to trace the flow of calls across APIs and organizations.

The trace ID could be used for example:

  • for operational purposes, to enable lookup related technical logs

  • for audit logging, in order to be able to trace the origin of each request. It may be a legal requirement to have such audit logging when sensitive data is exchanged.

Rule 53: HTTP headers for tracing

APIs SHOULD support the tracing HTTP header BelGov-Trace-Id on both requests and responses. Generated trace IDs MUST NOT be longer than 36 characters and unique. The trace id on a response MUST be generated by the API provider and is not copied from the request. Using UUIDs as tracing headers is RECOMMENDED.

Additionally, the BelGov-Related-Trace-Id HTTP header MAY be present on a response. Its value MUST be the same as the BelGov-Trace-Id on the request.

Within an organization, other headers MAY be used for tracing calls, for instance the headers supported by tracing tools like Zipkin or Jaeger.

In communication external to an organization, each BelGov-Trace-Id value should be unique. For instance, when a RESTful service makes multiple outgoing calls within the context of the same incoming request, every outgoing request should be uniquely identifiable with its own ID. For auditing purposes, it should be possible to correlate the various tracing IDs used in the context of a request.

An API provider should not be dependent on the client generating correct unique trace IDs.