REST Guidelines
2024-04-05
1. Introduction
This style guide for RESTful services is a collaborative effort by several Belgian government institutions, originally under the G-Cloud umbrella, before moving to Belgif, the Belgian Interoperability Framework. Its goal is to improve compatibility between RESTful services offered by government institutions or any other organization adopting these guidelines.
This guide is a living document, updated when new interoperability issues arise or when REST-related standards evolve. All changes are reviewed by the REST design workgroup, in which the Belgian government organizations are represented. If you find errors or omissions, or want to contribute, you can open an issue or pull request on GitHub.
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).
Topics not covered in this guide:
-
Securing REST APIs. Guidelines are under development by the REST Security Working Group, based on the OpenID Connect and OAuth 2.0 standards.
-
Semantics of common business data (e.g. social security number, address, …). These are managed by the REST Functional Working Group in the belgif fedvoc repository.
-
OpenAPI specifications for common data types can be found in the openapi-* GitHub repositories.
For brevity most URIs in examples are shortened, but in practice URIs should be in absolute notation. |
License
This work is licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
2. Changelog
-
2024-04-05
-
update [oas-tags]: add new guidelines on tags (declared, style, no more than one tag on an operation)
-
new: rule [oas-comp] applies naming guidelines on all types of components (previously in [oas-types] only for schemas)
-
added motivation for [oas-rdonly] why required properties shouldn’t be readOnly
-
clarify that health status should be from client point-of-view in Health
-
-
2024-01-31
-
new: a document may also be a singleton that’s not part of a collection (Singleton documents)
-
new: path parameter names should use lowerCamelCase [path-param]
-
update: clarify that query parameters should use lowerCamelCase [uri-notat]
-
update: the name of a multi-value query parameter should be singular if it’s a noun [qry-multi]
-
new: explicit rule and clearer guidelines on
GET /health
resource [hlth-res]
-
-
2023-10-05
-
update: [req-valid] issue type for unknown input is renamed
-
new: rule [hdr-case] on case-insensitivity of HTTP headers
-
update: explicit rules for guidelines: [meth-http] and [stat-codes] (use of appropriate HTTP methods and status codes), [col-sort] (sorting a collection)
-
update: improved overview table for the use of HTTP methods [meth-http]
-
update: corrected use of status 303: allowed on all methods in overview table [stat-codes]
-
update: links to the releases of common OpenAPI definitions are added to [oas-comdef]
-
update: Problem Details for HTTP APIs is updated to RFC 9457 which obsoletes RFC 7807 throughout the guide
-
-
2023-06-14
-
add /doc/openapi.[json,yaml] to list of reserved resources Resource names
-
updated [id-name]: naming convention for OpenAPI types for identifiers and codes
-
updated [hyp-links]: avoid cross-API links, make self links optional
-
new: [req-valid]: handling unknown request parameters
-
updated list of OpenAPI tools
-
-
2023-03-17
-
Rule identifier with anchor added for each rule in the guide
-
Updated [jsn-naming]: naming of JSON properties
-
Updated [id-name] with new naming conventions for identifiers and codes
-
Older changes
-
2022-12-07
-
new: Representing general problem types in OpenAPI
-
new: Representing general and operation-specific HTTP headers in OpenAPI
-
-
2022-10-27
-
updated: the response body when creating a resource may also contain partial or full resource (Create a new resource)
-
-
2022-05-20
-
move REST guide to belgif.be, updating href problem links
-
-
2022-03-31
-
new: use string based types for decimals when precision is important (Decimals)
-
updated: new type InputValidationProblem for problems of type
badRequest
andresourceNotFound
(breaking change) -
updated: clarify the use of multiple query parameters to filter a collection (Filtering)
-
-
2022-02-02
-
new: designing new identifiers and codes or using existing numerical ones
-
updates for OpenAPI 3.0 support throughout the entire guide
-
new: Service Unavailable problem type (http 503)
-
added: use Retry-After HTTP header in Too Many Failed Requests and Too Many Requests
-
-
2021-06-24
-
updated: use URNs as problem type and add href for problem documentation (Error handling)
-
updated: clarify use of charset in JSON media type (Media Types)
-
added: how to specify media types in OpenAPI 3 (OpenAPI 3.0)
-
-
2020-12-02
-
new: standardized problem types internalServerError, badGateway, tooManyFailedRequests, payloadTooLarge (Standardized problem types)
-
-
2020-11-04
-
added: Remove a selection of items from a collection (Collection)
-
updated: status codes made consistent in various locations (Collection, Document, HTTP Methods and Status codes)
-
added: status codes 429 Too Many Requests and 502 Bad Gateway (Status codes)
-
added: table about how to use each HTTP method (HTTP Methods)
-
added: table listing acceptable status codes for each method (Status Codes)
-
-
2020-09-09
-
new: recommend use of CloudEvents specification (Events)
-
add guidelines on API health checks (Health)
-
move common OpenAPI schemas to separate GitHub repositories (Common OpenAPI definitions)
-
-
2020-08-03
-
allow OpenAPI 3.0 (API specifications)
-
remove unmaintained swagger-validator-maven-plugin and styleguide-validationrules from tools list (Tools)
-
change status codes to string types in example OpenAPI definitions to be compliant to the OpenAPI standard
-
set license of this guide to Apache License, Version 2.0 (Introduction)
-
-
2020-03-24
-
add links to GitHub, vocabularies and OAS schemas in the Introduction
-
-
2020-01-10
-
updated rules: naming of JSON properties and naming of JSON data types: no digits as first letter and no dots in names
-
added 413 Payload Too Large to list of HTTP status codes
-
updated Resource URI notation rule: no trailing slashes in URIs
-
added rule: default values in OpenAPI specification
-
added Controller resource archetype and move all archetypes to Resources chapter
-
-
2019-05-23
-
update list of standard problem types and add InvalidParamProblem schema to problem-v1.yaml, see Standardized problem types
-
new rule: naming of identifiers in Document
-
add status code
303 See Other
to Create a new resource -
new rule: use tags in OpenAPI
-
new section: how to specify media types in OpenAPI
-
update rules: naming of JSON properties and naming of JSON data types to make names more meaningful
-
new rule: use
description
and avoidtitle
to describe JSON data types -
updated rule: String and integer types (JSON data types). String based codes should have a unique representation.
-
-
2019-01-22
-
modified API specifications guidelines for reusable definitions to be put in Swagger files instead of multiple smaller JSON schema files
-
gCloud JSON Schemas refactored to this new file structure
-
updated list of Swagger editors and code generators in Tools
-
added guidelines about Tracing with standardized HTTP headers
-
add OpenAPI example of multi-valued query param to Resources
-
added rule: place reference data resources in
/refData
(doc resource) -
add list of reserved resource names to Reserved words
-
document
select
reserved query param to filter resource representation (Consult (Document))
-
-
2018-11-26
-
add SelfLink type in Hypermedia controls
-
recommend American English for JSON property names and JSON data types
-
add recommendations on typing of properties in JSON data types (integer/string, enums)
-
-
2018-09-09
-
HttpLink type updated in Hypermedia controls to be read only instead of required
-
new rule in JSON data types for enum values
-
-
2018-08-09
-
Standardized Problem response for several use cases in Error handling and updated Problem examples throughout the guide
-
-
2018-08-08
-
added clarifications and example for use of
HttpLink
to Hypermedia controls -
changed guideline for JSON Schemas to single root type per file and add rule on common BelGov schemas
-
-
2018-06-29
-
added files with common data types containing types referenced throughout the guide
-
added MergePatch type to Partial update
-
added guidelines for Long-running tasks
-
-
2018-05-28
-
added Caching guidelines
-
-
2018-04-25
-
added Internationalization (I18N) guidelines
-
updated Controller, allowing verb as child resource and GET method in some cases
-
update format of version number in API specifications
-
added contact email address in Introduction
-
prefix
BelGov-
added for custom reserved HTTP headers
-
-
2018-04-19
-
guidelines about reusable JSON Schemas
-
-
2018-04-18
-
added Versioning guidelines
-
removed collector, added "Non-CRUD operations" under Document section
-
-
2018-03-14
-
added reserved HTTP headers and rule on custom http headers (reserved HTTP headers)
-
content negotiation and JSON subtypes added to Media Types
-
Consulting a collection: empty items array mandatory when collection is empty
-
relax the rule on
additionalProperties
to be compatible with its use for embedded resources
-
-
2018-03-07
-
Error handling: use of RFC 7807
-
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:
-
Resources – How can we express where the data is being transferred to or from?
-
HTTP Methods – What are the protocol mechanisms used to transfer the data?
-
Media Types – What type of data is being transferred?
3.2. 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.
URI = https://
host/
pathPrefix/
apiName/v
majorVersion/
resources
example: https://api.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 and SHOULD be 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.
-
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.
-
HTTP Methods — Level 2 introduces a standard set of verbs so that we handle similar situations in the same way, removing unnecessary variation.
-
Hypermedia controls — Level 3 introduces discoverability, providing a way of making a protocol more self-documenting.
All REST services MUST at least respect level 2 and desirably achieve level 3.
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/demo/v1/employers/406798006?lang=nl |
URIs must be intuitively structured.
URI fragments point to a part of a resource’s representation, and are never sent to the API, but handled locally by clients. They are thereby not specified in a REST API’s specifications (OpenAPI).
Path segments and query parameters within an API SHOULD use lowerCamelCase notation 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. Trailing slashes MUST NOT be used.
GOOD | BAD |
---|---|
https://api.socialsecurity.be/demo/v1/socialSecretariats/331 |
https://api.socialsecurity.be/demo/v1/Social_Secretariats/331 |
https://api.socialsecurity.be/demo/v1/socialSecretariats/331 |
https://api.socialsecurity.be/demo/v1/socialSecretariats/331/ |
https://api.socialsecurity.be/demo/v1/socialSecretariats?country=BE |
https://api.socialsecurity.be/demo/v1/Social_Secretariats?Country=BE |
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.
GOOD: https://api.socialsecurity.be/demo/v1/socialSecretariats/331
BAD: https://api.socialsecurity.be/demo/v1/socialSecretariats/331.json
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.
https://api.socialsecurity.be/demo/v1/socialSecretariats*?q=sdworx*
Filter the resource collection using a search string.
https://api.socialsecurity.be/demo/v1/socialSecretariats/331*?select=(name,address)*
Filter the resource properties to the ones specified in the select
query parameter.
https://api.socialsecurity.be/demo/v1/socialSecretariats/331*?lang=nl*
Only return translatable properties in Dutch.
When a single query parameter can have multiple values, the parameter SHOULD be repeated for each value.
If the parameter’s name is a noun, it SHOULD be singular.
GET /cars?color=black&color=blue
parameters:
- name: color
in: query
collectionFormat: multi
type: array
items:
type: string
parameters:
- name: color
in: query
# defaults for style and explode properties are OK (style: form, explode: true)
schema:
type: array
items:
type: string
4.2. Resource Archetypes
There are three different resource archetypes:
4.3. 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.
Documents in a collection
Most document resources are contained in a parent collection resource to which a variable path segment with identity key is added.
Location | Document URI | Resource representation | Path template |
---|---|---|---|
part of collection |
|
|
|
Singleton documents
Documents may also be singletons, of which only a single instance exists within the context of its parent, if any. A singleton document’s URI ends with a fixed path segment that is a singular noun.
Location | Document URI | Resource representation | Path template |
---|---|---|---|
singleton with document as parent |
|
image file (binary) |
|
singleton without a parent |
|
|
|
Child resources of a document
A document may have child resources of any type (document, collection or controller) that represent its specific subordinate concepts.
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.
4.3.1. Identifier
The identity key of a document resource is preferably a natural business identifier uniquely identifying the business resource like an ISBN or SSIN. If such key does not exist, a surrogate or technical key can be created.
New types of identifiers should be designed carefully. Once an identifier has been introduced, it may get widespread usage in various systems even beyond the scope for which it was initially designed, making it very hard to change its structure later on.
Designing identifiers in a URI structure, as specified in the ICEG URI standard, is useful as it makes the identifier context-independent and more self-descriptive. A REST API may choose to use a shorter API-local form of a URI identifier because of practical considerations.
When designing an identifier, various requirements may be of importance specific to the use case:
-
the governance and lifecycle of identifiers and the entities they represent
-
easy to memorize (e.g. textual identifier like problem types)
-
input by user (e.g. web form, over phone/mail)
-
easy to type (ignore special separator chars, difference between lower/capital case), limited length
-
validation of typing errors, e.g. by checksum, fixed length, …
-
hint on format to recognize purpose of identifier based on its value
-
-
printable (restricted length)
-
open to evolve structure for new use cases
-
ability to generate identifiers collision-free at multiple independent sources, e.g. by adding a source-specific prefix, using UUIDs, …
-
stable across different deployment environments (e.g. problem type codes)
-
hide any business information (e.g. no sequential number that indicates number of resources created)
-
not easy to guess a valid identifier, especially for unsecured resources (e.g. no sequentially generated identifier)
-
easy to represent in URL parameter without escaping
-
sortable (for technical reasons e.g. pagination)
For new identifiers, a string based format SHOULD be used: textual lowerCamelCase string codes, UUID, URI or other custom formats. Take into account the requirements that follow from the ways the identifier will be used.
Each identifier MUST be represented by only one single string value, so that a string equality check can be used to test if two identifiers are identical. This means that capitalization, whitespace or leading zeroes are significant.
In the OpenAPI data type for the identifier, a regular expression may be specified if helpful for input validation or as hint of the structure (e.g. to avoid whitespace or wrong capitalization), but shouldn’t be too restrictive in order to be able to evolve the format.
No business meaning SHOULD be attributed to parts of the identifier. This should be captured in separate data fields. Parts with technical meaning like a checksum are allowed.
Parts of an identifier may carry some business meaning for easier readability, like the problem type identifiers in this guide, but no application logic should parse and interpret these parts. |
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. |
The table below lists some examples of identifiers, though it does not list all possibilities or considerations when designing a new identifier.
identifier structure | example | OpenAPI type | considerations |
---|---|---|---|
UUID |
"d9e35127-e9b1-4201-a211-2b52e52508df" |
Type defined in common-v1.yaml
|
long identifier, not easy to memorize or input by user, easy to generate, resistant to brute-force guessing |
URI (URN) |
"urn:problem-type:belgif:resourceNotFound" |
|
can be human-readable, long, not easy to input by user |
URI (http) |
"https://www.waterwegen.be/id/rivier/schelde" |
|
can be human-readable, long, not easy to input by user, requires character escaping when used as URL parameter can be generated collision-free by multiple sources (different domain name) |
custom format |
"ab12347895" |
|
short, easy to encode |
A code is a special type of identifier:
-
it has an exhaustive list of possible values that doesn’t change frequently over time
-
each value identifies a concept (examples: a country, a gender, …).
New code types SHOULD be represented as string values in lowerCamelCase.
Depending on context, the OpenAPI data type may enumerate the list of allowed values (see Enum values rule).
GET /refData/paymentMethods/{code}
with code
of type PaymentMethodCode
As string with enumeration:
PaymentMethodCode:
type: string
enum:
- cash
- wireTransfer
- creditCard
- debitCard
As string with regular expression:
PaymentMethodCode:
type: string
pattern: "^[A-Za-z0-9]+$"
example: "debitCard"
When defining the type for a property representing an existing numerical code or identifier:
-
Identifiers that are commonly represented (e.g. when displayed or inputted by a user) with leading zeros present SHOULD be represented using a string type. A regular expression SHOULD be specified in the OpenAPI data type to avoid erroneous values (i.e. without leading zeros).
-
Otherwise, use an integer based type. It is RECOMMENDED to further restrict the format of the type (e.g.
format: int32
and usingminimum
/maximum
).
For new identifiers, it is not recommended to use a number type however as stated in [new-identifiers]
An employer ID may be of variable length. Leading zeroes are ignored and most of the time not displayed.
EmployerId:
description: Definitive or provisional NSSO number, assigned to each registered employer or local or provincial administration.
type: integer
format: int64
minimum: 0
maximum: 5999999999
example: 21197
If SSIN has a zero as first digit, it is always displayed.
Ssin:
description: Social Security Identification Number issued by the National Register or CBSS
type: string
pattern: '^\d{11}$'
Country NIS code is a three-digit code, the first digit cannot be a zero.
CountryNisCode:
description: NIS code representing a country as defined by statbel.fgov.be
type: integer
minimum: 100
maximum: 999
example: 150 # represents Belgium
Following naming guidelines should be applied when using an identifier or code in a REST API:
-
JSON property:
-
within an object that represents the entire or part of a resource: use
id
orcode
-
to reference a resource within another one’s representation: property name should designate the relation between the resources (see [jsn-naming]); no need to suffix with
id
orcode
-
-
path parameter: use
id
orcode
, OPTIONALLY prefixed with the resource type to disambiguate when there are multiple identifiers as path parameters -
query search parameter: use same name as the property in the JSON resource representation of the response (see Filtering)
-
OpenAPI type: add suffix
Id
orCode
to distinguish it from the type of the full resource representation
As an exception, use the standardized name for the business identifier if one exists, rather than id
or code
.
If multiple identifiers or coding schemes may be used within the same context, a suffix can be added to the name to disambiguate.
Request:
GET /stores/{storeId}/orders/{orderId} (1)
Response:
{
"id": 123, (2)
"customer": 456, (3)
"store": {
"id": 789, (4)
"href": "/stores/789"
},
"paymentMethod": "creditCard",
"deliveryMethod": {
"code": "deliveredAtHome", (5)
"href": "/refData/deliveryMethods/deliveredAtHome"
}
}
OpenAPI types (not all are listed for brevity):
Order: (6)
type: object
properties:
id:
$ref: "#/components/schemas/OrderId" (7)
customer:
$ref: "#/components/schemas/CustomerId" (7)
store:
$ref: "#/components/schemas/StoreReference" (8)
paymentMethod:
$ref: "#/components/schemas/PaymentMethodCode" (7)
deliveryMethod:
$ref: "#/components/schemas/DeliveryMethodReference" (8)
OrderId: (7)
type: integer
format: int32
minimum: 1
StoreReference: (8)
allOf:
- $ref: "./common/v1/common-v1.yaml#/components/schemas/HttpLink"
type: object
properties:
id:
$ref: "#/components/schemas/StoreId" (7)
StoreId: (7)
type: integer
format: int32
minimum: 1
1 | path parameter: id prefixed with resource type to be able to distinguish both parameters |
2 | id as property of the consulted resource |
3 | identifier used to reference another resource. JSON property name designates relation to the other resource. |
4 | id as property in a partial representation of a store resource |
5 | code as property in a partial representation of a deliveryMethod resource |
6 | The type of the full resource representation doesn’t have a suffix |
7 | Types of the identifiers have the suffix Id or Code |
8 | Partial resource representations, which may link to the full resource |
GET /refData/deliveryMethods/{code} (1)
{
"code": "deliveredAtHome",
"description": {
"nl": "Geleverd aan huis",
"en": "Delivered at home",
"fr": "Livré à domicile"
}
}
1 | Since there are no child resources with other path parameters, simply code suffices |
GET /persons/{ssin} (1)
{
"ssin": "12345678901", (1)
"partner": "2345678902", (2)
"civilStatus": 1
}
1 | Standardized business identifier name Ssin is preferred over id . |
2 | JSON property name designates relation to the other resource. The OpenAPI specification declares the expected value to be of type Ssin . |
GET /addresses/{addressId}
{
"municipality": {
"code": 10000,
"name": "Brussels"
},
"country": {
"nisCode": 150, (1)
"isoCode": "BE", (1)
"name": {
"nl": "België",
"fr": "Belgique"
}
}
}
1 | Prefixes nis and iso disambiguate between two types of country identifiers used in a single context |
4.3.2. Consult
/employers/{employerId} |
Consult a single employer |
|
Parameters |
||
|
path-param |
NSSO number uniquely identifying the employer. |
Response |
||
body |
The response properties and links to other resources. |
|
Most used response codes |
||
OK |
Default success code if the document exists. |
|
Bad request |
The dynamic path segment containing the identity key has a wrong data format:
|
|
Not Found |
The document resource does not exist. |
204 No content should not be used with GET. |
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> ::= "!"
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
}
}
}
4.3.3. 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:
-
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.
-
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 better modeled as separate sub-resources.
-
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 Controller 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.
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.
Absent optional values in the request are set to their default value if one is specified in the OpenAPI specification.
PUT https://api.socialsecurity.be/demo/v1/employers/93017373 HTTP/1.1
{
"employerId": 93017373,
"name": "Belgacom"
}
/employers/{employerId} |
Replace the entire employer resource with the client data. This implies a full update of the resource. Via |
|
Request |
||
body |
Full representation of the resource to persist. |
|
Parameters |
||
|
path-param |
employer ID of NSSO uniquely identifying the employer. |
Response |
||
body |
either empty or resource after update |
|
Most used response codes |
||
OK |
The update is successful and updated resource is returned. |
|
No Content |
The update is successful but updated resource is not returned. |
|
Bad request |
The input data is not valid according the data schema. |
|
Not Found |
The resource does not exist and thus cannot be updated. |
|
Conflict |
The client data is in conflict with the data on the server e.g. optimistic locking issues. |
Partial update
Use PATCH
when you like to do a partial update of a document resource.
The PATCH
message MUST 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.
PATCH https://api.socialsecurity.be/demo/v1/employers/93017373 HTTP/1.1
/employers/{employerId} |
Performs a partial update of an existing employer. |
|
Request |
||
body |
JSON Merge Patch |
|
Parameters |
||
|
path-param |
employer ID of NSSO uniquely identifying the employer. |
Response |
||
body |
empty or the complete resource after applying PATCH |
|
Most used response codes |
||
OK |
Success code with resource after applying PATCH returned. |
|
No Content |
Success code without returning the resource. |
|
Bad request |
The input data is not valid according the data schema. |
|
Not Found |
The resource does not exist and thus cannot be updated. |
|
Conflict |
The client data is in conflict with the data on the server e.g. optimistic locking issues. |
4.3.4. Remove
Use DELETE
when you like to delete a document resource.
DELETE https://api.socialsecurity.be/demo/v1/employers/93017373 HTTP/1.1
/employers/{employerId} |
Deletes an employer. |
|
Parameters |
||
|
path-param |
employer ID of NSSO uniquely identifying the employer. |
Response |
||
body |
empty or a message indicating success |
|
Most used response codes |
||
OK |
Success code with the deleted resource returned. |
|
No Content |
Success code with the deleted resource not returned. |
|
Bad request |
Generic error on client side. For example, the syntax of the request is invalid. |
|
Not Found |
The resource does not exist and thus cannot be deleted. |
|
Conflict |
A constraint on the resource is violated. The resource could not be deleted because the request is in conflict with the current state of the resource. For example, a REST API may return this response code when a client tries to DELETE a non-empty store resource. |
4.3.5. Long-running tasks
Some operations need to be performed asynchronously, as they take too long to complete.
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 theLocation
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.
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": "urn:uuid:d9e35127-e9b1-4201-a211-2b52e52508df",
"title": "Bad Request",
"status": 400,
"type": "urn:problem-type:example:invalidImageFormat",
"href": "https://example.org/example/v1/refData/problemTypes/urn:problem-type:example: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.
4.4. Collection
A collection resource is a server-managed list of document resources.
A plural noun SHOULD be used for collection names, for example 'employers' or 'people'.
4.4.1. Consult
The representation of a collection MUST contain a list of links to child resources:
-
A collection result 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
property MAY be used to provide a human-readable description for an item, usable as display text for the link. -
The number of items in the collection result set MAY be included in the response using the
total
property. If the response is paginated, its value MUST correspond to the number of items regardless of pagination, rather than only the number of items in the current page.
A collection resource, like other resources, MUST always return a JSON object as top-level data structure to support extensibility ([evo-object]). Do not return a JSON array, because the moment you like to add paging, hypermedia links, etc. your API will break. |
OK |
Default response code, also when the collection is empty. |
|
Bad Request |
Generic error on client side. For example, the syntax of the request is invalid. |
|
Not found |
The URI provided cannot be mapped to a resource. |
204 No content should not be used with GET. |
A sort
multi-value query parameter MAY be provided to allow a client to sort the items of a collection resource on one or more specified properties.
Descending sorting order can be indicated by prefixing the property name with -
.
The API SHOULD document which properties can be used for sorting, and whether descending order is allowed.
sort |
Multi-value query param with list of properties to sort on.
Direction is ascending by default. To indicate descending, prefix property with |
|
GET https://api.socialsecurity.be/demo/v1/employers HTTP/1.1
{
"self": "https://api.socialsecurity.be/demo/v1/employers",
"items":[
{
"href":"/employers/93017373",
"title":"Belgacom",
"employerId": 93017373,
"enterpriseNumber": "0202239951"
},
{
"href":"/employers/20620259",
"title":"Partena VZW",
"employerId": 20620259,
"enterpriseNumber": "0409536968"
}
],
"total":2
}
EmployersCollection:
allOf:
- $ref: common/v1/common-v1.yaml#/components/schemas/SelfLink
- type: object
properties:
items:
type: array
items:
$ref: "#/components/schemas/EmployerLink"
total:
type: integer
required:
- items
- total
EmployerLink:
allOf:
- $ref: common/v1/common-v1.yaml#/components/schemas/HttpLink
- type: object
properties:
employerId:
$ref: "./employer/identifier/v1/employer-identifier-v1beta.yaml#/components/schemas/EmployerId"
enterpriseNumber:
$ref: "./organization/identifier/v1/organization-identifier-v1.yaml#/components/schemas/EnterpriseNumber"
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.
{
"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."
}
}
}
}
AppendixCodesCollection:
description: A collection of appendix codes
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/AppendixCodeLink'
total:
type: integer
embedded:
type: object
additionalProperties:
$ref: 'appendixCode.yaml#/components/schemas/AppendixCode'
required:
- items
- total
AppendixCodeLink:
allOf:
- $ref: 'common/v1/common-v1.yaml#/components/schemas/HttpLink'
- type: object
properties:
value:
$ref: 'appendixCode.yaml#/components/schemas/AppendixCodeValue'
4.4.2. Filtering
A collection may support query parameters to filter its items:
-
a query parameter with the name of a document property, filters items on the specified value
-
the query parameter
q
is reserved for a full text search on all the document’s content -
other filter parameters may be supported, e.g. to look up items within a search period
Unless the API documentation explicitly states otherwise, returned collection items:
-
should satisfy ALL filter query parameters (AND-logic)
-
have to match ANY of the values of a multi-valued filter query parameter (OR-logic).
For example, the query
GET /cars?doors=5&color=black&color=blue
should be interpreted by default as: "search for all cars that have 5 doors AND are of color blue OR black".
/employers |
get all the employers documents in the collection |
|
Parameters |
||
name |
query-param |
Filter only employers that have a specific name. |
Response |
||
body |
|
|
Most used response codes |
||
OK |
Default response code, also when the filtered collection is empty. |
|
Bad Request |
Generic error on client side. For example, the syntax of the request is invalid. |
|
Not found |
The URI provided cannot be mapped to a resource. |
204 No content should not be used with GET. |
4.4.3. Pagination
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.
|
MANDATORY (except for the last page) |
hyperlink to the next page |
|
OPTIONAL |
hyperlink to the previous page |
|
RECOMMENDED |
Maximum number of items per page. For the last page, its value should be independent of the number of actually returned items. |
|
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) |
|
OPTIONAL |
hyperlink to the first page |
|
OPTIONAL |
hyperlink to the last page |
|
OPTIONAL |
Total number of items across all pages. If query parameters are used to filter the result set, this is the total of the collection result set, not of the entire collection. |
The names of the properties with hyperlink values and the items
property are derived from the IANA registered link relations.
|
OPTIONAL |
maximum number of items per page desired by client; must have a default value if absent. |
|
MANDATORY with default value 1 (offset-based); N/A (cursor-based) |
the index of page to be retrieved |
{
"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"
}
{
"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"
}
4.4.4. Create a new resource
The collection resource can be used to create new document resources from the POST
request body.
Absent optional values are set to their default value if one is specified in the OpenAPI specification.
POST |
/employers |
create a new employer in the collection |
Request |
||
body |
The data of the resource to create. |
|
Response headers |
||
Location |
http-header |
The URI of the newly created resource e.g. /employers/93017373 |
Response |
||
body |
|
The API should specify for each creation operation, if it returns:
|
Most used response codes |
||
Created |
Default response code if the resource is created. |
|
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). |
|
See Other |
The resource already exists.
May be returned instead of |
200 OK should not be used with POST for creating resources. |
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
4.4.5. Remove
A selection of items can be removed from a collection using the DELETE method. In fact, the collection itself cannot be removed, but it can be emptied if all its items are removed. By using query parameters, the items to be removed can be filtered. In order to remove a single specific item from a collection, use DELETE on the document resource.
/employers |
Delete all the employers documents in the collection. |
|
Parameters |
||
name |
query-param |
Remove only employers that have a specific name. |
Response |
||
body |
Empty or a message incidating success. |
|
Most used response codes |
||
OK |
The items are successfully removed from the collection and returned. |
|
No content |
The items are successfully removed from the collection but no additional content is included in the response body. |
|
Bad Request |
Generic error on client side. For example, the syntax of the request is invalid. |
|
Not found |
The collection resource does not exist and thus cannot be deleted. |
4.5. Controller
A controller resource models a procedural concept. Controller resources are like executable functions, with parameters and return values; inputs and outputs.
A REST API can rely on controller resources to perform application-specific actions that cannot be logically mapped to one of the standard methods (standard CRUD-operations).
A controller resource name is a verb instead of a noun.
The HTTP method on a controller 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
Withdrawal from an account:
POST /accounts/123/withdraw
Sending a notification to an employer:
POST /employers/93017373/sendNotification
Converting money from one currency to another (using GET because of no side effects):
GET /convertMoney?from=EUR&amount=45&to=USD
4.5.1. Controller vs document
Before using a controller resource to represent an action, consider reifying the action as a collection or document resource (noun) describing the intent of the action.
Long-running tasks are an example of actions modelled as a collection/document instead of a controller.
Withdrawal from an account as a controller resource:
POST /account/123/withdraw
or as a collection resource:
POST /account/123/withdrawals
Using a noun improves extensibility in this case. For instance, a GET operation could be added to consult a history of all withdrawals executed on the account.
5. HTTP Methods
5.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.
5.2. POST
POST must be used to create a new resource in a collection or to execute a controller resource.
When using for resource creation, 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 URI of the newly created resource, while the body contains either its (partial or full) representation or is empty.
POST operations may have side effects (i.e. modify state), and are not required to be idempotent.
5.3. PUT
PUT must be used to update a stored resource under a consumer supplied URI. It could be used to insert a new resource in case the client application decides on the resource identifier.
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.
5.4. 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.
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'.
5.5. 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.
5.6. HEAD
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.
5.7. OPTIONS
OPTIONS should be used to retrieve metadata that describes a resource’s available interactions.
5.8. How to use each method
The HTTP methods SHOULD be used that are appropriate for the type of action performed on the resource.
The table below lists the usages of HTTP methods for each resource archetype. Combinations explicitly excluded ( -
) SHOULD NOT be used.
Method | Document | Collection | Controller | Request body | Response body |
---|---|---|---|---|---|
Empty |
Resource(s) |
||||
- |
Representation of the created resource or controller info |
Optional |
|||
- |
- |
Representation of the updated resource |
Optional |
||
- |
- |
Fields of the resource to update |
Optional |
||
- |
Empty |
Optional |
|||
X |
X |
X |
Empty |
Empty |
|
X |
X |
X |
Empty |
Optional |
6. Status codes
6.1. Overview
The HTTP response status code SHOULD be returned that best describes the outcome of the treatment of the request.
The table below list most common combinations of a status code for each HTTP method, though may not be exhaustive. Combinations explicitly excluded ( -
) SHOULD NOT be used.
The full list of HTTP status codes is documented here.
This HTTP status codes chart (takes a while to load) shows a decision tree to determine the usage of the correct HTTP status code.
Code | GET | HEAD | PUT | POST | PATCH | DELETE | OPTIONS |
---|---|---|---|---|---|---|---|
X |
X |
X |
X (controller only) |
X |
X |
X |
|
- |
- |
X (creation only) |
X |
- |
- |
- |
|
- |
- |
- |
X |
- |
- |
X |
|
- |
X |
X |
X |
X |
X |
- |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
- |
- |
- |
- |
- |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
- |
|
X |
X |
X |
X |
X |
X |
X |
|
- |
- |
X |
X |
X |
X |
- |
|
- |
- |
X |
X |
X |
X |
- |
|
- |
- |
X |
X |
X |
- |
- |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
6.2. List of status codes
6.2.1. 1xx Informational
Request received, continuing process.
6.2.2. 2xx Success
The action was successfully received, understood, and accepted.
Code | Description | Methods |
---|---|---|
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. |
||
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. The response body may be empty or contain a partial or full representation of the new resource. 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. |
POST PUT only in case it is used to create a new document resource. |
|
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 (see Long-running tasks). A POST method may send 202 responses, but other methods should not. |
POST |
|
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, PATCH or DELETE request, when the REST API declines to send back any status message or representation in the response message’s body. |
6.2.3. 3xx Redirection
Further action must be taken in order to complete the request.
Code | Description | Methods |
---|---|---|
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”) 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. |
All methods are acceptable. Mostly used with POST. |
|
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”) 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 |
6.2.4. 4xx Client Error
The request contains bad syntax or cannot be fulfilled.
Code | Description | Method |
---|---|---|
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”) 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”) 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 |
||
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. |
All: GET, HEAD, PUT, PATCH, DELETE, OPTIONS, POST (if parent resource not found) |
|
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: |
||
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 |
||
409 (“Conflict”) should be used when a request conflicts with the current state of the target resource 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. |
||
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 |
||
413 (“Payload Too Large”) should be used when a request is refused because its payload is too large The 413 error response indicates that the request is larger than the server is willing or able to process. |
||
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 |
||
429 (“Too Many Requests”) should be used to indicate that the user has sent too many requests in a given amount of time. The response representations SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait before making a new request. Note that this specification does not define how the origin server identifies the user, nor how it counts requests. Responses with the 429 status code MUST NOT be stored by a cache. |
6.2.5. 5xx Server Error
The server failed to fulfill an apparently valid request.
Code | Description | Methods |
---|---|---|
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. |
||
The 502 ("Bad Gateway") status code indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request. |
||
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. |
7. Media Types
The HTTP protocol supports content negotiation for media types of the response.
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).
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). The choice for JSON is particularly made for supporting JavaScript clients.
Structured data MUST be made available in JSON format (RFC 8259) with media type application/json
.
JSON text MUST be encoded using UTF-8.
Some legacy clients erroneously decode JSON payloads using a non-UTF-8 encoding by default.
To work around this, a charset parameter may be added to the Content-Type
HTTP header, even though it has no meaning according to the JSON RFC.
Content-Type: application/json;charset=UTF-8
Media 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 9457) 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
Payload media types can be specified in an OpenAPI specification.
For JSON, the media type specified in OpenAPI should not contain a charset
parameter, because this is known to lead to problems in some server software when treating requests without the parameter in the Accept
header.
OpenAPI 2.0
The request and response body’s media type are specified in an OpenAPI 2.0 specification respectively by the consumes
and produces
keywords:
-
at operation level
-
at root level, as default media types for the entire specification
/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/v1/person-identifier-v1.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. |
OpenAPI 2.0 specifications SHOULD specify following default media types:
consumes:
- application/json
produces:
- application/json
- application/problem+json
OpenAPI 3.0
OpenAPI 3.0 allows to specify a different media type for each request or response format. Default media types cannot be specified.
/persons/{ssin}/photo:
get:
summary: Get the photo of the person
operationId: getPhoto
parameters:
- $ref: "person/identifier/v1/person-identifier-v1.yaml#/components/parameters/SsinQueryParameter"
responses:
"200":
description: successful operation
content:
image/jpeg:
schema:
type: string
format: binary
"400":
description: SSIN has invalid structure
content:
application/problem+json:
schema:
$ref: "#/components/schemas/InvalidSsinProblem"
"404":
description: SSIN was not found
content:
application/problem+json:
schema:
$ref: "#/components/schemas/UnknownSsinProblem"
default:
"$ref": "./problem/v1/problem-v1.yaml#/components/responses/ProblemResponse"
8. Performance
8.1. Embedding resources
In order to reduce the number of requests, optional embedding of subresources may be used.
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, so the only restriction that can be specified is type: object
when there are multiple embedded data types.
{
"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"
}
}
}
Family:
description: A family
type: object
properties:
parents:
type: array
items:
$ref: '#/components/schemas/PersonRef'
children:
type: array
items:
$ref: '#/components/schemas/PersonRef'
embedded:
type: object
additionalProperties:
$ref: 'person.yaml#/components/schemas/Person'
8.2. 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:
8.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.
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.
Last-Modified
header [prf-lstmod]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.
8.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.
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.
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.
|
9. JSON
All RESTful services must support JSON (Javascript Object Notation RFC 7159).
For extensibility, the payload (if any) of a RESTful request or response should always be a JSON object instead of an array, string, number, etc ([evo-object]). This way the request or response can always be extended with new properties ( [evo-compat]).
Following guidelines SHOULD be respected when determining a name for a JSON property:
-
use lowerCamelCase notation
-
also for abbreviations: all letters after the first should be lowercase
-
-
use American English
-
do not use underscores (_), hyphens (-) or dots (.) in a property name, nor use a digit as first letter
-
don’t use overly generic terms like
info(rmation)
anddata
as property name or as part of it -
the name should refer to the business meaning of its value in relation to the object in which it is specified, rather than how it is defined
-
omit parts of the name that are already clear from the context
Properties used from other standards, like OpenID Connect and OAuth2, are allowed to deviate from this rule.
KO | OK |
---|---|
SSIN |
ssin |
street_RRN |
streetRrn |
customerInformation |
customer |
descriptionStringLength140 |
description |
|
|
In below example, enterpriseNumber
should be used even though the enterprise
prefix is clear by its context.
This is because enterpriseNumber
is a fixed denomination (declared in Crossroads Bank for Enterprises) and using just number
would rather cause confusion to type of identifier being used.
{
"name": "Belgacom",
"employerId": 93017373,
"enterprise": {
"enterpriseNumber": "0202239951"
"href": "/organizations/0202239951"
}
}
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 |
|
|
The JSON properties have no specific order inside a JSON object.
|
|
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:ss.SSSXXX
See JSON Schema Validation 7.3.1. date-time and RFC 3339 section 5.6. The fraction part for sub second precision is optional and may be of any length.
{
"lastModification": "2016-04-24T09:26:01.5214Z"
}
{
"lastModification": "2016-04-24T11:26:00+02:00"
}
10. API specifications
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.
10.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.
Specifications of the API SHOULD be provided using OpenAPI 2.0 (aka Swagger) or OpenAPI 3.0. OpenAPI uses the OpenAPI Schema Object to describe the JSON representation of resources, which is a variant of JSON Schema, with some significant incompatibilities.
OpenAPI 3.1 improves upon OpenAPI 3.0, but to avoid interoperability problems it SHOULD NOT be used yet because it is not yet widely supported by most tooling.
The OpenAPI standard also includes a list of supported primitive data types.
You can visualize OpenAPI files using some of the tools listed in Tools.
Any additional documentation SHOULD be linked from the API’s OpenAPI definition using the externalDocs
property.
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.
Using tags will make the visual representation (e.g. SwaggerUI) more readable. Multiple tags on an operation duplicates the operation in SwaggerUI, which may cause confusion.
Declaring each tag allows linting tooling to detect inconsistencies in tag names.
tags:
- name: Employers
description: Managing employers
- name: Reference data
paths:
/employers/{employerId}:
get:
summary: Get an employer by its identifier
tags:
- Employers
# ...
/employers:
get:
summary: Find employers matching given search parameters
tags:
- Employers
- # ...
post:
summary: Add a new employer
tags:
- Employers
/refData/employerClasses:
get:
summary: Find enterprises matching given search parameters
tags:
- referenceData # BAD: doesn't match declared tag name, words aren't separated by space, doesn't start with capital letter
# ...
HTTP header parameters that may be present on all or most operations of an API SHOULD NOT be documented explicitly in the OpenAPI specification, as this would clutter the spec and make it difficult to keep up to date. Rather, refer API users to general documentation on these headers.
Operation-specific HTTP header parameters SHOULD be documented.
Following are examples of HTTP headers that shouldn’t be documented explicitly for each operation:
-
Accept-Language
-
BelGov-Trace-Id
andBelGov-Related-Trace-Id
(documented in Tracing) -
Authorization
Examples of headers that should be specified on operations that support them:
-
ETag
-
If-Modified-Since
The OpenAPI specification file and other API documentation SHOULD be placed under a special doc
resource under the API root location.
Access to these documentation resources SHOULD be granted to any user or potential user of the API.
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.
doc
and refData
resources /doc
/openapi.json
/openapi.yaml
/<optional other documentation>
/refData
/<list1OfCodes>
/<code1>
/<code2>
/...
/<list2OfCodes>
/...
/<resource1>
/...
/<resource2>
/...
...
default
values [oas-defaul]Absent optional properties in a request are set by the API provider to their default
value if one is specified in the OpenAPI specification.
Path parameters SHOULD use lowerCamelCase notation.
KO | OK |
---|---|
|
|
Add example response values to the OpenAPI specification under the examples
property.
OpenAPI 2.0
/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 3.0
/enterprises/{enterpriseNumber}:
get:
operationId: getEnterprise
parameters:
- in: path
name: enterpriseNumber
required: true
schema:
type: string
responses:
"200":
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Enterprise'
examples:
success:
{
"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 or specified using a x-examples custom extension following the OpenAPI 3.0 format.
|
Duplication of definitions (schemas, responses, parameters, etc.) SHOULD be avoided.
Rather, define them once under components
, from which they can be referenced (using $ref
).
All component names SHOULD be defined in American English and use UpperCamelCase notation. For abbreviations as well, all letters except the first one should be lowercased.
Do not use underscores (_), hyphens (-) or dots (.) in a component name, nor use a digit as first letter.
Instead of specifying everything directly in the openapi.yaml
(or swagger.yaml
) file of an API, OpenAPI allows to reference data types (schemas) and other components from other files.
These files SHOULD follow the Swagger/OpenAPI file format as well and may include data type (schema) definitions, but also other component types like parameters, path items, request bodies and responses.
To work around limitations of certain tools, a conversion step to inline the definitions into the openapi.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 its 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 file, doing so makes it possible to use standard OpenAPI/Swagger tooling on them.
openapi: "3.0.3"
info:
title: person-identifier
description: data types for person identifiers
version: "1.1.2"
paths: {} # empty paths property required to be a valid OpenAPI file
components:
schemas:
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#/components/schemas/Ssin"
Common definitions for use by Belgian government institutions are maintained in the openapi-* GitHub repositories, released as zip archives, organized per domain and also available in Maven Central. 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 openapi-common and openapi-problem repositories. Other types for business concepts commonly used by Belgian government institutions are available in other repositories.
10.2. JSON data types
As stated in [oas-comp], data type (schema) names should use UpperCamelCase and American English.
In addition, data type names SHOULD NOT include overly generic terms like info(rmation)
and data
.
A data type name SHOULD refer to the business meaning rather than how it is defined.
KO | OK |
---|---|
SSIN |
Ssin |
CustomerInformation |
Customer |
LanguageEnumeration |
Language |
The description
property MAY provide a textual description of a JSON data type.
The title
property MUST NOT be used because it hides the actual data type name in visualization tools like Swagger UI.
KO | OK |
---|---|
|
|
additionalProperties
can be used to put restrictions on other properties of a JSON object than those specified in the schema.
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 Embedding resources.
Other uses of additionalProperties
than for maps are to be avoided, in order to support schema evolution.
An API SHOULD refuse unknown input, i.e. unknown body (JSON) properties in the payload or unknown query request parameters. A Bad Request problem response should be returned with an issue of type urn:problem-type:belgif:input-validation:unknownInput
.
If an operation does allow and process input that is not defined in OpenAPI, its description should explicitly indicate this.
In specific situations, where a (known) input is not needed anymore and can be safely ignored:
-
either it can stay in the API definition with a deprecation flag and a "not used anymore" description
-
or it can be removed from the API definition as long as the server ignores this specific input.
Unknown HTTP header parameters MUST be accepted.
If unknown request fields would be accepted, certain client errors cannot be recognized by servers, e.g. parameter name typing errors will be ignored and the client’s actual intent will not be met.
Unknown HTTP headers are usually metadata added automatically by technical components that do not change the API’s expected behavior and thereby can be ignored.
Properties SHOULD be declared as readOnly: true
when appropriate.
This means that the value of the property is managed exclusively by the owning authority, and attempts by a client to modify the value of this property are expected to be ignored or rejected.
Examples are an immutable identifier of a document, properties that are computed from other properties, or that represent a volatile state of a resource.
Properties marked as readOnly
being true
SHOULD NOT be in the required
list of the defined schema.
Above guideline is compliant with the definition of readOnly
used by the OpenAPI 3.1 Specification.
The OpenAPI 3.0 Specification has a different definition, that allows to omit properties from requests that are both readOnly
and required
. This has been changed in OpenAPI 3.1 to achieve full compatibility with the JSON Schema Specification. Hence, to be forward-compatible, OpenAPI documents should not depend on this 3.0 behavior.
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.
State:
type: string
enum:
- processing
- failed
- done
When defining a type for an identifier or code, like the above example, the guidelines under Identifier apply, even when not used as a URL path parameter of a document resource.
Decimal numbers for which the fractional part’s precision is important, like monetary amounts, SHOULD be represented by a string
-based type, with number
as format. Depending on the context, a regular expression can enforce further restrictions like the number of digits allowed before/after comma or on the presence of a +
/-
sign.
When number
would be used as type instead of string
, some technologies will convert the values to floating point numbers, leading to a loss of precision and unintended calculation errors.
This problem may also be avoided by using an equivalent integer representation, for example by expressing a monetary amount in Euro cent rather than Euro.
Some more background on why floating point numbers can lead to loss of precision, can be found in this blog post.
belgif openapi-money defines a string-based type for monetary values:
MonetaryValue:
type: string
format: number # number is a custom string format that is supported by some, but not all tooling
pattern: '^(\-|\+)?((\d+(\.\d*)?)|(\.\d+))$' # Variable number of digits, with at least one digit required, before or after the decimal point. Allows both positive and negative values.
x-examples:
- "100.234567"
- "010"
- "-.05"
- "+1"
- "10"
- "100."
MonetaryAmount:
description: A monetary amount
type: object
properties:
value:
"$ref": "#/components/schemas/MonetaryValue"
currency:
"$ref": "#/components/schemas/Currency"
required: [value, currency]
example:
value: "0.01"
currency: "EUR"
It also defines integer-based types specific for monetary amounts expressed in Euro cent:
EuroCentPositiveAmount:
description: Money amount in Euro cents >= 0
type: integer # representation as Euro cent instead of Euro to avoid floating point rounding problems and need for custom 'number' format
minimum: 0
EuroCentAmount:
description: 'Money amount in Euro cents, also allows negative amounts.'
type: integer # representation as Euro cent instead of Euro to avoid floating point rounding problems and need for custom 'number' format
10.3. Tools
Following tools can be used to edit OpenAPI files
Name | Link | Description |
---|---|---|
Swagger UI |
Browser application. Graphical and text view of Swagger files. Does not support references to external files. |
|
Zalando’s Swagger plugin |
Open Source plugin for IntelliJ. Text-only editor. |
|
Stoplight Studio |
Commercial editor with a free version. Graphical and text view, both web based or as desktop application. Supports validation of API style guides (Spectral). |
|
42Crunch OpenAPI (Swagger) Editor for VS Code |
https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi |
Open Source plugin for Visual Studio Code. Text editor with SwaggerUI preview and multi-file support. |
42Crunch OpenAPI (Swagger) Editor for IntelliJ |
https://plugins.jetbrains.com/plugin/14837-openapi-swagger-editor |
Plugin for IntelliJ. Text editor with multi-file support. |
IntelliJ OpenAPI Specifications |
Plugin bundled with IntelliJ Ultimate (commercial). Text editor with SwaggerUI preview and multi-file support. |
Following tools can be used to generate server stubs and API client libraries from OpenAPI specification files.
Name | Link | Comments |
---|---|---|
openapi-generator |
Started as fork of swagger-codegen. |
|
swagger-codegen |
10.4. References
Name | Link |
---|---|
OpenAPI 2.0 specification |
|
OpenAPI 3.0 specification |
|
Swagger |
11. 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.
11.1. Links
When referencing another resource within the same API, a link SHOULD be added using the href
attribute.
It is NOT RECOMMENDED to link to resources in other APIs to avoid a tight coupling between the APIs, unless the link target is guaranteed to remain very stable (like the href
links to standard problem types [err-problem]).
Each resource MAY 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:
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:
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.
{
"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.
components:
schemas:
Company:
allOf:
- "$ref": "./common/v1/common-v1.yaml#/components/schemas/SelfLink"
- type: object
properties:
owner:
"$ref": "#/components/schemas/PersonReference"
website:
type: string
format: uri
PersonReference:
allOf:
- "$ref": "./common/v1/common-v1.yaml#/components/schemas/HttpLink"
- type: object
properties:
ssin:
"$ref": "./person-identifier/v1/person-identifier-v1.yaml#/components/schemas/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.
{
"self": "/companies?page=2&pageSize=10",
"prev": "/companies?page=1&pageSize=10",
"next": "/companies?page=3&pageSize=10"
}
The use of Web Linking and Hypertext Application Language (HAL) is not recommended.
12. Reserved words
A list of reserved words exists for common used practices
12.1. Query parameters
Term | Description | Example | Ref |
---|---|---|---|
When a collection resources is paged, use this parameter to request a specific page. Page numbers are 1-based. |
?page=3&pageSize=20 |
||
When a collection resources is paged, use this parameter to specify the page size. |
?page=3&pageSize=20 |
||
The standard search parameter to do a full-text search. |
?q=Belgacom |
||
Filter the resource properties to the ones specified. |
?select=(name,address) |
||
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=age&sort=-name |
||
embed |
Request to embed subresource |
?embed=mainAddress |
|
language to filter multi-language descriptions |
?lang=fr |
12.2. JSON properties
Term | Description | Example | Ref |
---|---|---|---|
The next-reference contains the absolute URL of the next page in a paged collection result. |
|
||
The previous-reference contains the absolute URL of the previous page in a paged collection result. |
|
||
The self-reference contains the absolute URL of the resource itself. |
|
||
A reference (absolute URL) to another resource. |
|
||
A reference (absolute URL) to the first page in a paged collection result. |
|||
A reference (absolute URL) to the last page in a paged collection result. |
|||
items |
an array with the items of a collection result. |
||
total |
the total number of items in a collection result, after filtering. |
||
page |
the index of a page in a paged collection result |
||
pageSize |
the maximum number of items in a page of a paged collection result. |
||
embedded |
a map of embedded subresources, with URIs as property key and the resource as value |
12.3. HTTP headers
This list includes all standardized and common non-standard HTTP headers.
Applications MUST treat HTTP header names as case-insensitive as per the HTTP specification.
However, by convention, Kebab-Case with uppercase SHOULD be used for readability and consistency.
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.
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. |
|
Content-Type |
Request/Response |
The Media type of the body of the request. |
|
X-HTTP-Method-Override |
Request |
Override the method specified in the request. |
|
Retry-After |
Response |
Suggest amount of time for the client to wait before retrying the request when temporarily unavailable or quota reached |
Service Unavailable, Too Many Requests, Too Many Failed Requests |
Allow |
Response |
Valid methods for a specified resource. |
|
ETag |
Request |
Identifier for returned response content |
|
If-None-Match |
Response |
Return resource if ETag changed |
|
Last-Modified |
Request |
Time on which resource was last modified |
|
If-Modified-Since |
Response |
Return resource only if changed since specified timestamp |
|
Vary |
Response |
Specifies which request headers change response content |
|
Cache-Control |
Response |
Indicates HTTP client how to cache responses |
HTTP Header | Type | Description | Reference |
---|---|---|---|
BelGov-Trace-Id |
Request/Response |
Unique ID for tracing purposes, identifying the request or response |
|
BelGov-Related-Trace-Id |
Response |
BelGov-Trace-Id value used on related request |
12.4. Resource names
path | Description | Reference |
---|---|---|
/doc |
API documentation (swagger file and other) |
|
/refData |
resources representing reference data used in the API (i.e. code lists) |
|
/health |
API health status |
13. Error handling
13.1. Problem Details
Problem Details for HTTP APIs (RFC 9457, obsoletes RFC7807) 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
-
a suitable status code: 4xx for consumer or 5xx for server errors
-
a RFC 9457 problem detail message
Problem:
description: A Problem Details object (RFC 9457)
type: object
properties:
type:
type: string
format: uri
description: An absolute URI that identifies the problem type
default: about:blank # kept for backwards-compatibility, type will be mandatory in problem-v2
href:
type: string
format: uri
description: An absolute URI that, when dereferenced, provides human-readable documentation for the problem type (e.g. using HTML).
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: An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
Any information on a problem SHOULD be provided in the Problem Detail format, as specified in Problem Details for HTTP APIs (RFC 9457, which obsoletes RFC 7807) :
-
the media type for problems SHOULD be
application/problem+json
-
status
: the HTTP status code -
type
: a unique identifier for the type of the problem that MUST remain stable over time. It can be used by API clients during problem handling. -
title
: a short summary of the problem type in English -
instance
: a unique identifier of the problem instance. It could be used to track the message for support purposes. -
detail
: consumer-friendly information about the specific failing request. It MAY be localized using language negotiation. -
additional properties may be added to provide more information on the problem
The Problem Details response body MAY be absent when the HTTP status code itself provides sufficient information (e.g. for 405 Method Not Allowed, 406 Not Acceptable or 415 Unsupported Media Type).
This guide applies following additional restrictions on the Problem Details structure:
-
type
andinstance
SHOULD be specified as absolute URIs in order to avoid issues when resolving relative URIs (see Links) -
The
type
property, a stable identifier for the type of problem, is MANDATORY instead of optional -
a new optional
href
property SHOULD be used to provide a URI to human-readable documentation on the problem type instead of dereferencing thetype
value
Note that using href
instead of type
for documentation intentionally deviates from the recommendation in the RFC.
href
allows use of a URL for documentation purposes that may change over time, while type
can be specified as a URN that must remain stable.
This is especially useful for API-specific problem types for which the documentation URL may depend on technical aspects, like deployment environment.
POST /enterprises/0206731645/employers
{
"name": "John"
}
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
BelGov-Trace-Id: d9e35127-e9b1-4201-a211-2b52e52508df
{
"type": "urn:problem-type:belgif:resourceNotFound",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
"instance": "urn:uuid:d9e35127-e9b1-4201-a211-2b52e52508df",
"status": 404,
"title": "Resource is not found",
"detail": "There is no enterprise in CBE with enterprise number 0206731645",
"issues": [
{
"in": "path",
"name": "enterpriseNumber",
"detail": "the enterprise number is not assigned",
"value": "0206731645"
}
]
}
Problem type
SHOULD be specified as a URN in one of following formats:
-
urn:problem-type:<org>:<api>:<type>
-
urn:problem-type:<org>:<type>
with:
-
<org>
: a short identifier for the organization -
<api>
: name of the API. It MAY be used for API-specific problem types -
<type>
: type of the problem in lowerCamelCase
A description of an API-specific problem type MAY be provided as a reference data resource : /refData/problemTypes/{problemType}
.
If provided, at least the HTML media type SHOULD be supported.
Reverse FQDN notation for <org>
is not recommended in order to keep the identifiers short and readable.
{
"type": "urn:problem-type:cbss:socialStatus:searchCriteriaTooWide",
"href": "https://api.ksz-bcss.fgov.be/socialStatus/v2/refData/problemTypes/urn:problem-type:cbss:socialStatus:searchCriteriaTooWide",
"status": 400,
"title": "Search criteria should be more specific",
"detail": "Only one search parameter specified. Please specify at least two.",
"instance": "urn:uuid:123e4567-e89b-12d3-a456-426614174000"
}
A problem MUST NOT 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
{
"type": "urn:problem-type:belgif:internalServerError",
"instance": "urn:uuid:ac19acc6-5e11-4b2a-8c10-f9680998d07a",
"title": "Internal Server Error",
"detail": "Unexpected error code 24879 in server product XYZ v1.0.1",
"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)"
]
]
}
Problem types that may occur on all or most operations of an API (e.g. a missing permission, an internal server error, request violates schema, …) SHOULD NOT each be documented explicitly in its OpenAPI specification, as this would clutter the spec and make it difficult to keep up to date.
Rather, a default Problem response SHOULD be added to each operation. Operation-specific problem types should still be documented.
Specifying a default problem response ensures compatibility with clients that perform strict OpenAPI validation on response messages.
ProblemResponse
from problem-v1.yaml)/orders/{orderId}/archive:
post:
summary: archive the order
operationId: archiveOrder
parameters:
- "$ref": "#/components/schemas/OrderId"
responses:
"200":
"$ref": "#/components/responses/OrderArchivedResponse"
"409":
description: Order can not be archived when it's not completed
content:
application/json:
schema:
"$ref": "#/components/schemas/InvalidStateProblem"
default:
"$ref": "./problem/v1/problem-v1.yaml#/components/responses/ProblemResponse"
13.2. Standardized problem types
A list of problem types is standardized in this guide for reuse across all REST APIs.
They are defined as URNs of format urn:problem-type:belgif:<type>
.
href links to descriptions of standardized problem types will change in the future to recommended format containing full problem type identifier without HTML extension. Current URLs will redirect.
|
13.2.1. Bad Request
Status code 400 Bad Request
Description The input message is incorrect. Look for more details in the issues
property.
The following issue types may be returned by any API:
Issue type | description |
---|---|
|
violation of the OpenAPI schema |
|
Request contains an unknown input field (see [req-valid]) |
POST /enterprises/abc
{
"name": "exampleEnterprise",
"boardMembers": [
{
"ssin": "12345678901",
"period": {
"startDate": "2020-12-31",
"endDate": "2020-01-01"
}
}
]
}
returns
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:badRequest",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
"title": "Bad Request",
"status": 400,
"detail": "The input message is incorrect",
"instance": "urn:uuid:123456-1234-1235-4567489798",
"issues": [
{
"type": "urn:problem-type:belgif:input-validation:schemaViolation",
"title": "Input isn't valid with respect to schema",
"detail": "enterpriseNumber abc should be numeric",
"in": "path",
"name": "enterpriseNumber",
"value": "abc"
},
{
"type": "urn:problem-type:cbss:input-validation:replacedSsin",
"href": "https://example.cbss.be/problems/replacedSsin",
"title": "SSIN has been replaced. Use new SSIN.",
"detail": "SSIN 12345678901 has been replaced by 23456789012",
"in": "body",
"name": "boardMembers[0].ssin",
"value": "12345678901",
"replacedBy": "23456789012"
},
{
"type": "urn:problem-type:cbss:input-validation:invalidPeriod",
"title": "Period is invalid",
"detail": "endDate should be after startDate",
"in": "body",
"name": "boardMembers[0].period",
"value": {
"startDate": "2020-12-31",
"endDate": "2020-01-01"
}
}
]
}
InputValidationProblem:
type: object
allOf:
- $ref: "#/components/schemas/Problem"
properties:
issues:
type: array
items:
$ref: "#/components/schemas/InputValidationIssue"
InputValidationIssue:
type: object
description: |
An issue detected during input validation.
`status` is usually not present.
`href`, if present, refers to documentation of the issue type.
Additional properties specific to the issue type may be present.
allOf:
- $ref: "#/components/schemas/Problem"
properties:
in:
type: string
description: The location of the invalid input
enum:
- body
- header
- path
- query
name:
type: string
description: The name of the invalid input
value:
description: The value of the erroneous input
# no type specified, allowing any type. This is valid in OpenAPI even though some editors may indicate an error
The possible type
values used within issues
should be documented for each API. They follow the same URN structure as problem types. It is RECOMMENDED to use input-validation
as infix to distinguish them.
InputValidationProblem
replaces InvalidParamProblem
previously used in this guide, which is now deprecated.
13.2.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": "urn:problem-type:belgif:noAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/noAccessToken.html",
"status": 401,
"title": "No Access Token",
"detail": "No Bearer access token found in Authorization HTTP header"
}
13.2.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": "urn:problem-type:belgif:invalidAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/invalidAccessToken.html",
"status": 401,
"title": "Invalid Access Token",
"detail": "The Bearer access token found in the Authorization HTTP header is invalid"
}
13.2.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": "urn:problem-type:belgif:expiredAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/expiredAccessToken.html",
"status": 401,
"title": "Expired Access Token",
"detail": "The Bearer access token found in the Authorization HTTP header has expired"
}
13.2.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": "urn:problem-type:belgif:missingScope",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/missingScope.html",
"status": 403,
"title": "Missing Scope",
"detail": "Forbidden to consult the enterprise resource",
"requiredScopes": ["enterprise-read"]
}
13.2.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": "urn:problem-type:belgif:missingPermission",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/missingPermission.html",
"status": 403,
"title": "Missing Permission",
"detail": "Not permitted to update the details of this enterprise"
}
13.2.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 OpenAPI specification.
GET /enterprises
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:resourceNotFound",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
"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 issues
property.
GET /enterprises/0206731645
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:resourceNotFound",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
"status": 404,
"title": "Resource not found",
"detail": "No resource /enterprises/0206731645 found",
"issues": [
{
"in": "path",
"name": "enterpriseNumber",
"detail": "the enterprise number 0206731645 is not assigned",
"value": "0206731645"
}
]
}
The InputValidationProblem Schema Object SHOULD be used to represent this type of problems.
13.2.8. Payload Too Large
Status code 413 Payload Too Large
Description The consumer request was refused because its payload is too large.
The property limit
contains the maximum payload size expressed in bytes.
POST /attachments
<very large binary attachment>
returns
HTTP/1.1 413 Payload Too Large
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:payloadTooLarge",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/payloadTooLarge.html",
"status": 413,
"title": "Payload Too Large",
"detail": "Request message must not be larger than 10 MB",
"limit": 10485760
}
13.2.9. Too Many Requests
Status code 429 Too Many Requests
Description The consumer quota was exceeded.
The property limit
contains the number of requests allowed.
retryAfter
or retryAfterSec
indicate when the consumer may submit new requests again, respectively at a specific time or as a number of seconds after the reply.
The Retry-After HTTP header conveys the same information.
Retry after a specific time
HTTP/1.1 429 Too many requests
Content-Type: application/problem+json
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT
{
"type": "urn:problem-type:belgif:tooManyRequests",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyRequests.html",
"status": 429,
"title": "Too Many Requests",
"detail": "No more requests accepted before 2021-08-05T10:30:00Z",
"limit": 200,
"retryAfter": "2021-10-30T10:30:00Z",
"retryAfterSec": 60
}
Retry after a number of seconds
HTTP/1.1 429 Too many requests
Retry-After: 60
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:tooManyRequests",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyRequests.html",
"status": 429,
"title": "Too Many Requests",
"detail": "No more requests accepted during next 60 seconds",
"limit": 200,
"retryAfterSec": 60
}
13.2.10. Too Many Failed Requests
Status code 429 Too Many Requests
Description No more new requests are accepted because the consumer quota for failed requests was exceeded.
The property limit
contains the number of failed requests allowed.
retryAfter
or retryAfterSec
indicate when the consumer may submit new requests again, respectively at a specific time or as a number of seconds after the reply.
The Retry-After HTTP header conveys the same information.
GET /enterprises/123invalidEnterpriseId
Retry after a specific time
HTTP/1.1 429 Too many requests
Content-Type: application/problem+json
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT
{
"type": "urn:problem-type:belgif:tooManyFailedRequests",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyFailedRequests.html",
"status": 429,
"title": "Too Many Failed Requests",
"detail": "No more requests are accepted because of previous failures until 2021-08-05T10:30:00Z",
"limit": 50,
"retryAfter": "2021-08-05T10:30:00Z"
}
Retry after a number of seconds
HTTP/1.1 429 Too many requests
Retry-After: 300
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:tooManyFailedRequests",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyFailedRequests.html",
"status": 429,
"title": "Too Many Failed Requests",
"detail": "No more requests are accepted because of previous failures during next 300 seconds",
"limit": 50,
"retryAfterSec": 300
}
13.2.11. Internal Server Error
Status code 500 Internal Server Error
Description The server has encountered a situation it doesn’t know how to handle.
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:internalServerError",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/internalServerError.html",
"status": 500,
"title": "Internal Server Error"
}
13.2.12. Bad Gateway
Status code 502 Bad Gateway
Description The API, acting as a gateway or proxy to an API provided by a third party, received an invalid response from the upstream server.
HTTP/1.1 502 Bad Gateway
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:badGateway",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/badGateway.html",
"status": 502,
"title": "Bad Gateway",
"detail": "Error in communication with upstream service"
}
13.2.13. Service Unavailable
Status code 503 Service Unavailable
Description The service is unavailable, because of a planned or unplanned downtime.
If present, retryAfter
or retryAfterSec
indicate when to retry, respectively at a specific time or as a number of seconds after the reply.
The Retry-After HTTP header conveys the same information.
Retry after a specific time
HTTP/1.1 503 Service Unavailable
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:serviceUnavailable",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
"instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
"status": 503,
"title": "Service is unavailable",
"detail": "The service is unavailable due to a scheduled maintenance, and is expected to be available again at 10h30 on Aug 5th, 2021",
"retryAfter": "2021-08-05T10:30:00Z"
}
Retry after a number of seconds
HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:serviceUnavailable",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
"instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
"status": 503,
"title": "Service is unavailable",
"detail": "The service experiences an unplanned downtime. Please try again after 120 seconds.",
"retryAfterSec": 120
}
Without indication when to retry
HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
{
"type": "urn:problem-type:belgif:serviceUnavailable",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
"instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
"status": 503,
"title": "Service is unavailable",
"detail": "The service experiences an unplanned downtime."
}
14. Versioning
14.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.
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 |
14.2. API evolution
In a request or response body, if any, you MUST always return a JSON object (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:
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 a URL has to change (301 Moved Permanently)
-
a new resource variant (with another name) can be created in addition to the old resource variant
Note that while clients must ignore unknown response fields, APIs may refuse requests containing unknown fields ([req-valid]).
Deprecation MAY be used to indicate parts of an API are not recommended to be used anymore. Their deprecation SHOULD be documented in OpenAPI . 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.
Newer versions of the OpenAPI standard have improved support to indicate deprecation using deprecated: true
:
-
OpenAPI 2.0 only allows specifying deprecation on operations
-
OpenAPI 3.0 adds support for deprecating parameters and Schema Objects (data types), though not yet everywhere like on object properties
deprecated: true
SHOULD be used if the OpenAPI version allows it, otherwise the custom OpenAPI extension x-deprecated: true
SHOULD be used.
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.
14.3. API versioning
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).
swagger: "2.0"
info:
title: petShop
description: API exposing my pet shop’s functionality
version: "2.1.2"
host: example.org
basePath: /petShop/v2
openapi: "3.0.3"
info:
title: petShop
description: API exposing my pet shop’s functionality
version: "2.1.2"
servers:
url: https://example.org/petShop/v2
Similar guidelines apply to sunset an old major version of an API apply as for removal of deprecated elements.
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.
15. 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.
15.1. Accept-Language
As for Media Types, HTTP supports content negotiation for the language used in response content.
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.
Accept-Language: nl-BE, nl, fr-BE, fr, en, *
Content-Language: nl
15.2. Multi-language descriptions
It may be decided to include all supported translations for descriptions in a resource’s representation.
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:
description: A description specified in multiple languages
type: object
properties:
fr:
type: string
nl:
type: string
de:
type: string
GET /countries/BE
"name": {
"fr": "Belgique",
"nl": "België",
"de": "Belgien"
}
GET /countries/BE?lang=fr
"name": {
"fr": "Belgique"
}
16. 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.
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.
17. Events
When systems are in need of subscribe-notify capabilities, they tend to define their own custom event format.
To improve interoperability, this styleguide promotes and adopts the CloudEvents specification prepared by the Cloud Native Computing Foundation (CNCF): https://cloudevents.io/.
On October 24, 2019 they released a v1.0 version of this specification which thoroughly separates the core conceptual event model from any format and protocol details. Moreover, some SDKs are in development to support integration of CloudEvents in some popular programming languages.
Description | Link |
---|---|
CloudEvents overview |
|
Conceptual model |
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md |
JSON Event Format |
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/json-format.md |
HTTP Protocol Binding |
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md |
Web Hooks for Event Delivery |
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/http-webhook.md |
Extensions (Sequence, Claim Check, Partitioning, …) |
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/documented-extensions.md |
18. Health
Each REST API SHOULD expose a GET /health
operation which returns the availability of the API’s functionality for its clients.
Access to this resource SHOULD be granted to any client of the API.
The payload SHOULD be a JSON object with a property status
of type string
, that has one of the standardized values below, or a custom value for other API-specific use cases.
It SHOULD NOT contain any implementation or infrastructure details for security reasons, unless called by a trusted privileged client.
The HTTP response status SHOULD be either 200 OK
when the service is up or partially available, or 503 Service Unavailable
when the service is down or out of service.
Clients SHOULD interpret any other HTTP 5xx status, or lack of HTTP response, as the API being unreachable.
Status | Status Code | Description |
---|---|---|
UP |
200 |
The API is functioning as expected. |
DEGRADED |
200 |
The API is partly unavailable but service can be continued with reduced functionality. |
DOWN |
503 |
The API is suffering unexpected failures. |
The health status typically depends on the availability of multiple components used to provide the API’s functionality (e.g. application container, database, external web service dependency, …). It differs from a container liveness status, which only reflects the availability of a single component. |
If the health checks triggered by calling GET /health are resource-intensive, API implementations should consider caching the result. This may also protect against denial-of-service (DoS) attacks if the operation can be called from untrusted sources.
|
{
"status": "UP"
}
{
"status": "DOWN"
}
When called by a trusted privileged client, the response may contain additional properties with detailed information.
{
"status": "DOWN",
"details": {
"datastore": {
"status": "DOWN",
"errorMessage": "connection timeout"
}
}
}
The health resource is specified in common-v1.yaml. Note that uppercase is used for the status values, which deviates from [cod-design], in order to align with existing health checks provided by frameworks like Spring Boot and MicroProfile Health. The format of additional component-level details is not specified.