The API strategy consists of a core — a generic set of rules for all government APIs — and various extensions that only pertain to a specific application or that are not maure enough for the core set.
These extensions are described in this docuement. It is indicated whether an extension is stable or in development.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current Geonovum publications and the latest revision of this document can be found via https://www.geonovum.nl/geo-standaarden/alle-standaarden(in Dutch).
Dit is een door de werkgroep goedgekeurde consultatieversie. Commentaar over dit document kan gestuurd worden naar geo-standaarden@geonovum.nl.
Ten opzichte van de vorige versie van de API strategie (15-7-2019) is de status terug gezet naar consultatie versie. Dit om beter weer te geven dat nog actief aan de API designrules extensions gewerkt wordt. De huidige versie bevat dan ook een aantal inhoudelijke veranderingen ten opzichte van de versie van 15-7-2019. De sectie API security is behoorlijk aangepast en er is een sectie Signing & Encryption bijgekomen
Dit onderdeel is niet normatief.
This document contains the extensions on the API Designrules standard.
This document is part of the "Nederlandse API Strategie"
Part | Description | Status | Link |
---|---|---|---|
I | General description of the API Strategy | Informative | https://docs.geostandaarden.nl/api/API-Strategie/ |
IIa | Standard for designing APIs | Normative | https://docs.geostandaarden.nl/api/API-Designrules/ |
IIb | Extension on the Standard for designing APIs | Informative | https://docs.geostandaarden.nl/api/API-Strategie-ext/ |
Dit onderdeel is niet normatief.
The working group has indicated this extension to be stable.
Text of the extension...
Dit onderdeel is niet normatief.
This extension is still under development and can be modified at any time.
Text of the extension...
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
APIs can be accessed from any location on the internet. Information is only exchanged over TLS-based encrypted connections. No exceptions, so everywhere and always. One should follow the HTTP Strict Transport Security (HSTS) policy that mandates the use of HTTPS only.
Should we not just use NCSC guidelines for HTTPS? https://www.ncsc.nl/documenten/publicaties/2019/mei/01/ict-beveiligingsrichtlijnen-voor-transport-layer-security-tls
For Identification of individual users always use a pseudonym to avoid exposing sensitive information about a user. This pseudonym can optionally be translatable to actual personal information in a separate service, but access to this service should be tightly controlled and limited only to cases where there is a legal need to use this information. Use of a Burgerservice nummer(BSN) is only allowed when the organization has the right to do this. Even when an orgnization has the right to do this it is still reccomended to use a pseudonym that is only translatable to a BSN for a limited number of services/users within the organization. ** link naar DSO ontwerp?**
For identifying government organizations use the "organisatie-identificatienummer" (OIN) For identifying non-government organizations (companies, associations, foundations etc...) use the Handelsregisternummer (HRN) These are used in the PKIOverheid and e-Herkenning context. See https://www.logius.nl/diensten/oin for more information on these identifiers. OINs can be queried using the COR API https://portaal.digikoppeling.nl/registers/corApi/index HRNs are derived from the RSIN which can be queried in the "Handels register" https://developers.kvk.nl/documentation/search-v2
In the EU context use the eIDAS legal identifier. for more information see https://ec.europa.eu/digital-single-market/en/trust-services-and-eid
We distinguish end user authentication methods and system authentication methods.
The following authentication methods can be used end user authentication: Out of band When distributing API tokens to users an out of band authentication method can be used. Common methods include an API store where a user logs in and is able to acquire an API key. In this case the login method is the out-of-band authentication method used for accesing an API.
Openidconnect A Dutch profile for OpenIDConnect is currently being drafted. The first version will be for web usecases, later on it will be extended to cover mobile. When this profile is complete it is expected its use will be mandated by the "pas toe of leg uit lijst" of Forum standaardisatie.
SAML The underlying current Authentication method for DigiD & eHerkenning. It can be the basis as the out-of band method mentioned earlier. other usecases are.... (further elaboration needed)
The following authentication methods can be used for system authentication:
PKIOverheid These are x509 certificates derived from a root certificate owned by the Dutch Government. for more information on PKIOverheid see https://www.logius.nl/diensten/pkioverheid In the API context use only server or services certificates that include an OIN/HRN for identification. Extended validation certificates (as used for websites) do not include this identifier and are therefor not suitable for use with APIs.
Out of band When distributing API tokens to users an out of band authentication method can be used. Common methods include an API store where a user logs in and is able to acquire an API key. In this case the login method is the out-of-band authentication method used for accesing an API.
OAuth This is a standard for authorisation not authentication yet in some cases its use for machine to machine authentication can be appropriate. The client credentials authorization grant to be specific ...... (further elaboration needed)
When webbrowsers can be clients for an API these APIs should be compatible with the following policies and standards.
Web browsers implement a so-called "same origin policy", an important security conect to prevent requests go to another domain than where they are provided. While this policy is effective to prevent requests in different domains, it prevents ligitimate interaction between an API and clients from a known and trusted origin.
Content Security Policy is a standard that allows API (and website) providers to define approved origins of contents that are accesable through an API but are not provided from the same origin as the API itself. When multiple origins are needed this standard can be use as a mechanism to explicitly provide exceptions to the CORS-policy.
Subresource integrity (SRI) is a standard that can be used to validate content delivered by a third party. It defines trusted location and a hash of external content. This allows a client to verify the integrity and trustworthyness of external content accessed through an API.
Dit onderdeel is niet normatief.
This extension is still in development and can be modified at any time.
Major API releases should always be backward incompatible. In case a new API release does not result in backward incompatibility, it does not warrant a full version increase and it is merely a minor release. In case a major release takes place, all (potential) clients have to implement the new version.
As clients cannot be broken, a migration from the previous to the new release cannot happen abruptly. After the launch the new version, the previous version has to be available in production as well. As not to maintain the previous version indefinitely and to encourage clients to use the new version, a transition period is communicated for users to adjust their code to the new version. This period is referred to as deprecation period. The duration of this period may very per API. Typically, the deprecation period lasts 6 months, but not more than one year. From a maintenance perspective, at most two major versions (one of which is the deprecated version) should be available in production concurrently. Communication with users is crucial during this period. The following points have to be communicated:
These should be communicated using the following channels:
Warning
response header in all responses from the old API.A step-by-step approach:
Warning
response header to the old version;The Warning
reponse header (see: RFC 7234) has the status code 299 ("Miscellaneous Persistent Warning") and the API endpoint (including version number) as the warn-agent of the warning, followed by the warn-text with the human-readable warning. For example:
Waarschuwing: 299 https://service.../api/.../v1 "Deze versie van de API is verouderd en zal uit dienst worden genomen op 2020-02-01. Raadpleeg voor meer informatie hier de documentatie: https://omgevingswet.../api/.../v1".
Users should have sufficient time to phase out the old API. A period of 6 to 12 months is recommended.
Dit onderdeel is niet normatief.
This extension is still in development and can be modified at any time.
JavaScript Object Notation (JSON) is a format, just like XML, to serialise, store and transfer data. JSON is the primary representation format for APIs. For more information about JSON, read https://json.org. In contrast to XML, JSON has a compact notation, for example:
{
"persoon": {
"naam": "Jan",
"geboortejaar": 1983
}
}
snake_case
, camelCase
, UpperCamelCase
, or kebab-case
?Use camelCase
for field names.
Most REST clients and browsers (whether or not using extensions) can display JSON nicely formatted, even if the response does not include white-space.
Many APIs encapsulate responses in envelopes like below:
{
"persoon": {
"naam": "Jan",
"geboortejaar": 1983
}
}
A future-proof API does not have envelopes.
API principle: Send a JSON-response without enclosing envelope
POST
, PUT
, and PATCH
payloadsAPIs should at least support JSON-encoded POST
, PUT
, and PATCH
payloads. Encoded form data (application/x-www-form-urlencoded
) payloads are not supported. What is the difference?
Content-Type: application/json
results in:
{
"Name": "John Smith",
"Age": 23
}
and Content-Type: application/x-www-form-urlencoded
results in: Name=John+Smith&Age=23
API principle: Support JSON-encoded
POST
,PUT
, andPATCH
payloads
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
Base URLs of resources should be as straightforward as possible. Complex result filters, sorting, and advanced querying (restricted to a single resource) are implemented as query paramters on top of the base URL.
Filtering uses unique query parameters that correspond to the queryable fields. If you would like to request a list of applications from the end point /aanvragen/
and limit this to open applications, use the request GET /aanvragen?status=open
gebruikt. In this example status
is a queryable field.
API principle: Use query parameters corresponding to the queryable fields
This appreoach can be applied to nested properties as well. This is the collection:
[{
"id": 1,
"status": "actief",
"overheid": {
"code": "0000",
"naam": "Ministerie van BZK"
}
}, {
"id": 2,
"status": "inactief",
"overheid": {
"code": "9901",
"naam": "Provincie Gelderland"
}
}]
All objects with the status actief can be filtered using /?status=actief
. Filtering objects with code 0000 of the property overheid at the same time, this relates to a nested property. In this case, use the dot notation (like JavaScript): /?status=actief&overheid.code=0000
.
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
To sort, use the query parameter sorteer
. This query parameter takes a comma-separated list of field names to be used in the sort. Prefixing a filed name with a minus sign (-), the field is sorted in descending order. A few examples:
Request | Explanation |
---|---|
GET /aanvragen?sorteer=-prio |
Retrieves a list of aanvragen sorted by the property prio in descending order. |
GET /aanvragen?sorteer=-prio,aanvraagDatum |
Retrieves a list of aanvragen sorted by the property prio in descending order. Within a specific value of prio, the objects are sorted by aanvraagDatum in ascending order. |
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
Sometimes, straightforward filters are not sufficient and the capabilities of full-text search engines are required. APIs support full-text search using the query parameter zoek
. The result is returned in the same representation.
API principle: Use the query parameter
zoek
for full-text search
Examples combining filtering, sorting, and searching:
Request | Explanation |
---|---|
GET /aanvragen?sorteer=-wijzigingDatum |
Retrieves a list of aanvragen sorted by wijzigingsDatum in descending order |
GET /aanvragen?status=gesloten&sorteer=-wijzigingDatum |
Retrieves a list of aanvragen filtered by the status property set to gesloten and sorted by wijzigingsDatum in descending order |
GET /aanvragen?zoek=urgent&status=open&sorteer=-prio,aanvraagDatum |
Retrieves a list of aanvragen containing the word urgent and filtered by the status property set to open. The list is sorted by prio in descending order and subsequently sorted by aanvraagDatum in ascending order. |
APIs supporting full-text searching may take two types of wildcard characters:
*
Matches 0 or more (non-space) characters?
Matches exactly one (non-space) characterFor example, he*
matches any string starting with he
, like hek
, hemelwaterafvoer
, et cetera. The search string he?
only matches 3-letter strings that start with he
, like hek
, heg
, et cetera.
API principle: Support both
*
and?
wildcard characters for full-text search APIs
Find below some base rules for wildcard searches:
m*??
matches any string that starts with m
and has three or more characters.%20
) are used as word boundaries and wildcard matching only operates within a single word. For example, r*te*
matches the string r
uim
te
lijk
, but not the phrase r
uimte
te
kort
.To improve the API experience, common queries should be provided as end point. For example, aanvragen that have the status property set to gesloten and sorted by wijzigingsDatum in descending order, i.e. recently closed applications, can be retrieved using the following end point:
GET /aanvragen/recent-gesloten
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
Information about a resource may change over time. Time travelling is a default mechanism to request information about a specific moment in time is. From the perspective of time travelling using an API, there are three important moments in time:
Moment in time | Description |
---|---|
Geldig | This is a moment in time on which the data returned is valid (geldig) in reality. |
Beschikbaar | This is a moment in time on which the data returned is available (beschikbaar) using the same API. |
In werking (getreden op) | This is a moment in time on which the data returned became legally effective (in werking getreden op). |
The base principle for time travelling is as follows:
The values of these three query parameters in the URI are structured like this (based on RFC3339 / ISO 8601):
Parameter | Value |
---|---|
geldigOp |
YYYY-MM-DD |
beschikbaarOp |
YYYY-MM-DDThh:mm:ss.s |
inWerkingOp |
YYYY-MM-DD |
Value | Description |
---|---|
YYYY |
Four-digit year |
MM |
Two-digit month (01 = January, etc.) |
DD |
Two-digit day of the month (01 to 31) |
hh |
Two-digit hour (00 to 23) (am / pm not allowed) |
mm |
Two-digit minute (00 to 59) |
ss |
Two-digit second (00 to 59) |
s |
One or more digits to represent the decimal fraction of a second |
Time travelling is an optional mechanism with optional features. It may be that:
Users have to be informed of the invalid/not-supported requests when processing time travelling requests:
Not supported? | HTTP status code and explanation |
---|---|
Time travelling | HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type": ".../id/<c>/parameter/TijdreizenNietOndersteund", "title": "{Het verzoek is begrepen, maar de tijdreisparameters worden niet ondersteund}", "status": 403, "detail": "{Tijdreizen wordt niet ondersteund, verwijder alle tijdreisparameters}", "instance": "/resource/#id"} |
Legally effective | HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type":".../id/<c>/parameter/InWerkingTredingNietOndersteund’", "title": "{Het verzoek is begrepen, maar de tijdreisparameter ‘inWerkingOp’ wordt niet ondersteund}", "status": 403, "detail": "{Tijdreizen met een inwerkingstredingsmoment wordt niet ondersteund, verwijder de parameter ‘inWerkingOp’}", "instance": "/resource/#id"} |
Future | HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type": ".../id/<c>/parameter/ToekomstNietOndersteund", "title": "{Het verzoek is begrepen, maar tijdreisparameters mogen geen tijdstip in de toekomst bevatten” }", "status": 403, "detail": "{Tijdreizen naar de toekomst wordt niet ondersteund, verwijder de tijdreisparameters of gebruik een tijdstip in het verleden}", "instance": "/resource/#id"} |
Users have to be informed of the lack of historical data and general requests in a resource collection when processing time travelling requests:
Request | HTTP status code and explanation |
---|---|
Specific resource | HTTP/1.1 404 Content-Type: application/problem+json Content-Language: nl {"type": ".../id/<c>/BestaatNiet", "title": "De gevraagde versie bestaat niet.", "status": 404, "detail": "Versie 2017-01-01 van regeling 123 bestaat niet.", "instance": "/regelingen/v1/123?geldigOp=2017-01-01"} |
Resource-collectie | GET .../regelingen/v1?beschikbaarOp=2017-01-01T00:00:00 HTTP/1.1 200 Content-Type: application/json+hal {"_links": {"self": {"href": "/r-n/v1/123?beschikbaarOp=2017-01-01T00:00:00" }, "items": [ { "href": "/r-n/v1/123?beschikbaarOp=2017-01-01T00:00:00" }, { "href": "/r-n/v1/456?beschikbaarOp=2017-01-01T00:00:00" }, { "href": "/r-n/v1/789?beschikbaarOp=2017-01-01T00:00:00" }] } } |
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
REST APIs for handling spatial geometries may provide spatial filtering. There is a distinction between retrieving geometries in the result (response) and supplying a spatial filter in the call (request). When requesting parcel information, users do not necessarily require the geometry. A name or parcel ID may be sufficient.
For GEO APIs, preferably use the GeoJSON standard [rfc8142].
In a JSON API the geometry is returned as GeoJSON, wrapped in a separate GeoJSON object.
API principle: Include GeoJSON as part of the embedded resource in the JSON response
A spatial filter can be complex and large. It is best practice to supply complex queries in the body, not in the request URI. Since GET
may not have a payload (although supported by some clients) use a POST
request to a separate endpoint. For example, a GEO query to all panden where the geometry in the field _geo
(there may be multiple geometry fields) contains a GeoJSON object (in this case a Point
, so one coordinate pair):
// POST /api/v1/panden/_zoek with request body:
{
"_geo": {
"contains": {
"type": "Point",
"coordinates": [5.9623762, 52.2118093]
}
}
}
The POST
endpoint is preferably set up as a generic query endpoint to support combined queries:
// POST /api/v1/panden/_zoek with request body:
{
"_geo": {
"contains": {
"type": "Point",
"coordinates": [5.9623762, 52.2118093]
}
},
"status": "Actief"
}
Other GEO queries like intersects
or within
operators can be used as well. For further information, read: https://www.w3.org/TR/sdw-bp/#entity-level-links
API principle: Put results of a global spatial query in the relevant geometric context
For example:
// POST /api/v1/_zoek:
{
"_embedded": {
"results": [{
"type": "enkelbestemming",
"_links": {
"self": {
"title": "Enkelbestemming 1234",
"href": "/enkelbestemmingen/1234"
}
}
}, {
"type": "dubbelbestemming",
"_links": {
"self": {
"title": "Dubbelbestemming 8765",
"href": "/dubbelbestemmingen/8765"
}
}
}]
}
}
The default CRS (Coordinate Reference System) for GeoJSON is WGS84. This is the global coordinate reference system that can be applied world-wide. Due the datum and the tectonic displacements it is not accurate enough for local coordinate reference systems like ETRS89 (EPSG:4258, European), or RD/Amersfoort (EPSG:28992, Dutch).
Since most client-side mapping libraries use WGS84, the W3C/OGC working group Spatial Data on the Web recommends to use this as the default coordinate reference system. Thus, spatial data can be mapped without any complex transformations. The API strategy caters for this supporting not only ETRS89 and RD/Amersfoort, but also WGS84 and Web Mercator (EPSG:3857).
API principle: Use ETRS89 as the preferred coordinate reference system (CRS)
The CRS can be specified for request and response individually using custom headers: RD/Amersfoort, ETRS89, WGS84, and Web Mercator.
The guiding priciples for CRS support:
5.962376256, 52.255023450
)195427.5200 311611.8400
The following headers are purely meant for negotiation between the client and the server. Depending on the application, the request not only contains geometries but also specific meta data, e.g. the original realistion including the collection date.
Request and response may be based on another coordinate reference system. This applies the HTTP-mechanism for content negotiation. The CRS of the geometry in the request (request body) is specified using the header Content-Crs
.
HTTP header | Value | Explanation |
---|---|---|
Content-Crs |
EPSG:4326 | WGS84, global |
Content-Crs |
EPSG:3857 | Web Mecator, global |
Content-Crs |
EPSG:4258 | ETRS89, European |
Content-Crs |
EPSG:28992 | RD/Amersfoort, Dutch |
The preferred CRS for the geometry in the response (response body) is specified using the header Accept-Crs
.
HTTP header | Value | Explanation |
---|---|---|
Accept-Crs |
EPSG:4326 | WGS84, global |
Accept-Crs |
EPSG:3857 | Web Mercator, global |
Accept-Crs |
EPSG:4258 | ETRS89, European |
Accept-Crs |
EPSG:28992 | RD/Amersfoort, Dutch |
Certified software is available to the national government to transform between coordinate reference systems.
API principle: Use content negotiation to serve different CRSs
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
For content identified with the media type 'application/hal+json', Hypertext Application Language (HAL) is used for paging. Two reserved fields, _links
(required) and _embedded
(optional) are added to retrieved objects. These fields represent hyperlinks and embedded resources respectively:
{
"_links": {
"self": {
"href": "https://.../api/registratie/v1/aanvragen?pagina=3"
},
"first": {
"href": "https://.../api/registratie/v1/aanvragen"
},
"prev": {
"href": "https://.../api/registratie/v1/aanvragen?pagina=2"
},
"next": {
"href": "https://.../api/registratie/v1/aanvragen?pagina=4"
},
"last": {
"href": "https://.../api/registratie/v1/aanvragen?pagina=5"
}
},
"id": "https://.../api/registratie/v1/aanvragen/12",
"naam": "Mijn dakkapel",
"samenvatting": "Ik wil een dakkapel bouwen!",
"_embedded": {
"aanvrager": {
"naam": "Bob"
}
}
}
In case of plain JSON, GeoJSON, or something different from HAL, the _links
field is omitted. These may be added to the link response headers. Besides the representation the following metadata is returned using HTTP headers.
HTTP header | Explanation |
---|---|
X-Total-Count (optional) |
Total number of results |
X-Pagination-Count (optional) |
Total number of pages |
X-Pagination-Page (optional) |
Current page |
X-Pagination-Limit (optional) |
Number of results per page |
For large collections, the generation of X-Total-Count
and X-Pagination-Count
may have a considerable impact on the performance, particularly if there is no or limited filtering.
API principle: Use JSON+HAL with media type
application/hal+json
for pagination
All links in HAL are absolute. This relieves clients from the burder to resolve URLs to external links (to other endpoints, linked-data resources, etc.) for navigation.
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
For some resources, caching is required. HTTP provides default mechanisms for caching. Furthermore, both clients and infrastructure already handle these mechanisms by default.
The requirements to leverage these default caching mechanisms:
There are 2 ways to implement caching: ETag
and Last-Modified
.
An ETag (Entity Tag) is a hash code or checksum of a resource. A modification of a resource results in another ETag. Hence, an ETag is unique to a particular version of a resource. Returning a resource, the ETag is added as the HTTP header ETag
. The client caches both the resource and the ETag. Requesting the same resource, this ETag is supplied in the HTTP request header If-None-Match
. The server matches the ETag in the HTTP request header If-None-Match
to the ETag for the resource available to the server. In case these match, the server returns a HTTP response status code 304 Not Modified
terug. The client retrieves the resource from its own cache. This only applies to client-side caching, in case the client sends the request headers along, otherewise it receives an HTTP status code 200 OK
.
It works just like ETag, but uses timestamps instead. The response HTTP header Last-Modified
contains a timestamp that corresponds to the RFC 1123 notation and is valided against the request HTTP header If-Modified-Since
. The server checks whether the resource has been modified since the timestamp provided. If not, the server returns an HTTP status code 304 Not Modified
. The client retrieves the resource from its own cache. This only applies to client-side caching, in case the client sends the request headers along, otherewise it receives an HTTP status code 200 OK
.
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
APIs limit the amount of requests that can be sent within a particular time frame to prevent server overload and to guarantee a high service level. APIs may track a rate limit (quota) per month that is enforced per 60 second time interval.
HTTP headers communicate the rate limit to the users.
HTTP header | Explanation |
---|---|
X-Rate-Limit-Limit |
Amount of client requests per time frame |
X-Rate-Limit-Remaining |
Amount of remaining client request in the current time frame |
X-Rate-Limit-Reset |
Amount of remaing seconds in the current time frame |
[rfc6585] introduces an HTTP status code 429 Too Many Requests
to inform users the rate limit has been reached.
API keys are "unrestricted" by default. There are no usage restrictions and these API keys should therefore not be exposed in a web application. Using API keys without usage restrictions in JavaScript creates a real change for abuse and quota theft. To prevent this, restricted API keys should be issued and used.
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
Like a Web page displays useful error messages to visitors in case of an error, an API should return useful error message in a known and manageable format. The representation of an error is no different from the representation of a random resource, but with a particular set of fields.
APIs should always return useful HTTP status codes. HTTP status codes are divided in two categories:
A JSON representation of an error should contain a few details to assist developers, operators, and users:
The base for these default formats is [rfc7807]. A JSON-representation of an error is formulated like the following object:
{
"type": "URI: https://content.omgevingswet.overheid.nl/id/<c>[/{categorie}]/{fout}",
"title": "Hier staat wat er is misgegaan",
"status": 401,
"detail": "Meer details over de fout staan hier",
"instance": "urn:uuid:ebd2e7f0-1b27-11e8-accf-0ed5f89f718b" // The error instance
}
Validation errors for POST
, PUT
, and PATCH
requests are specified per field. The full list of errors is returned concurrently. The response consists of a fixed top level and error code for validation error and additional error fields with detailed errors per field:
{
"type": "https://content.omgevingswet.overheid.nl/id/<c>/ValidatieFout",
"title": "Hier staat wat er is misgegaan…",
"status": 400,
"invalid-params": [{
"type": "https://content.omgevingswet.overheid.nl/id/<c>/validatie/Voornaam",
"name": "voornaam",
"reason": "De voornaam mag geen speciale karakters bevatten."
}, {
"type": " https://content.../<c>/fouten/validatie/Wachtwoord",
"name": "wachtwoord",
"reason": "Het wachtwoord is verplicht."
}],
"instance": "urn:uuid:4017fabc-1b28-11e8-accf-0ed5f89f718b" // De fout-instantie
}
HTTP defines a range of default status codes for APIs. These assist users to of the APIs to handle errors.
Summary of HTTP operations and the primary HTTP status codes:
Operation | CRUD | Full collection (e.g. /resource ) specific item (e.g. /resource/\<id> ) |
---|---|---|
POST |
Create | 201 (Created), HTTP header Location with the URI to the new resource (/resource/\<id> )405 (Method Not Allowed), 409 (Conflict) in case the resource already exists |
GET |
Read | 200 (OK), list of resources. Use paging, filtering and sorting to ease the handling of large collections 200 (OK) single resource, 404 (Not Found) if the ID does not exist or is invalid |
PUT |
Update | 405 (Method Not Allowed), except for the purpose to modify or replace every resource in a collection 409 in case a modification is not possible due to the current state of an instance 200 (OK) or 204 (No Content), 404 (Not Found) if the ID does not exist or is invalid |
PATCH |
Update | 405 (Method Not Allowed), except for the purpose to replace the full collection. 409 if a modification is not possible due to the current state of an instance. 200 (OK) or 204 (No Content), 404 (Not Found) if the ID does not exist or is invalid |
DELETE |
Delete | 405 (Method Not Allowed), except for the purpose to remove the full collection. 200 (OK) or 404 (Not Found) if the ID does not exist or is invalid |
A short list and description of HTTP status codes:
HTTP status code | Description |
---|---|
200 OK | Response to a successful GET , PUT , patch or DELETE . Also suitable for POST that does not result in a creation |
201 Created | Response to a POST that results in a creation. Should be combined with a location header that points to the location of the newly created resource |
204 No Content | Response to a successful request that does not return content (e.g. a DELETE ) |
304 Not Modified | If HTTP caching headers are applied |
400 Bad Request | The request is invalid, e.g. in case the request (body) cannot be interpreted |
401 Unauthorized | If no or invalid authentication credentials are supplied. Also useful to display an authentication window if the API is used in a Web browser |
403 Forbidden | Response to a successful authentication, but the verified users is not authorised to access the resource |
404 Not Found | Response to a request for a non-existing resource |
405 Method Not Allowed | Response to an HTTP method that is not allowed for the authenticated user |
406 Not Acceptable | Reponse to an unsupported format request (part of content negotiation) |
409 Conflict | The request cannot be handled due to a conflict with the current state of the resource |
410 Gone | Indicates the resource is no longer available at the requested endpoint. Useful top level response to requests for previous API versions |
412 Precondition Failed | The preconditions supplied in one or more fields in the request header have been omitted or failed upon validation by the server |
415 Unsupported Media Type | If the wrong content type is supplied as part of the request |
422 Unprocessable Entity | Response to a request (body) that is correct but that cannot be handled by the server |
429 Too Many Requests | Response if the rate limit has been exceeded. |
500 Internal Server Error | If an unexpected error occured and server cannot respond. |
503 Service Unavailable | If an API is not available (e.g. due to planned maintenance) |
Dit onderdeel is niet normatief.
This extension is in development and may be modified at any time.
Signing and encryption are mechanisms that can provide additional security on top of the transport layer security that is provided by HTTP TLS in combination with some form of authentication and authorization.
In this API strategy we concern ourselves with RESTful APIs. In RESTful APIs you request the current value of a resource. This is different from SOAP based protocols where content is transported using messages. Using SOAP part of or all of the content is signed and/or encrypted by the sender of the message. This has utitlity as there are no pre conceptions about resources and their behaviour. When using REST you have to ask yourself what is being signed or encrypted. The value of a resource at a certain date and time. Does this have utility above and beyond the transport layer security thats already there?
Be aware that signing and encryption are "expensive". There is a performance hit (especially for encryption), but most of all in the real world implementation of an API using these security methods will be more expensive. Knowledge among programmers is ussually not complete, leading to extra time learning new skills. The added complexity leads to all manner of extra possibilities to create incorrect code, meaning testing will take longer.
Have a long hard look at alternatives to the technical solution of signing and encryption before commiting to using them.
Signing can be used to achieve:
Generally speaking there are three distinct types of signature: Detached, Enveloping and Enveloped. A signature can be detached, that is the signature is separate from the content it is signing. Signatures can be enveloping, that is the content being signed is contained within the signature itself. The last option is an enveloped signature, that is the signature structure/element is contained wihin the content that is signed.
Canonicalization(C14n) ensures that syntactically irrelavant information such as trailing whitespaces, different line terminators or tabs do not invalidate a signature. This is especially important with detached signatures where a different rendering of (semantically) the same content can invalidate a signature. A C14n algorithm ensures semantically identical content receives the same signature.
When signing takes place within an encoding format the way information is exchanged(e.g. through APIs) is not directly relevant to the way signatures are applied. The API design rules do not currently express any normative preference for encoding formats. We do have the JSON extension that expresses a non normative preference for JSON. Therefor we do include information on signing within the two most common encoding formats (XML & JSON) themselves even if it is not directly related to APIs.
The standard for XML digital signatures allows for Detached, enveloping and enveloped signatures of any XML format. https://www.w3.org/TR/xmldsig-core1/ When using a specific XML encoding format (or instance GML for geograpic information) one should be aware that when this specific XML format was not made with digital signatures in mind it may be very difficult to implement digital signatures. For instance for enveloped XML digital signatures most implementations expect the signature element as either the first or last element of the XML root element. An encoding standard such as GML currently does not allow users to add a signature element to the root of an xml document. Making XML signatures practically unimplementable using standard software.
JWT security best practices: https://datatracker.ietf.org/doc/draft-ietf-oauth-jwt-bcp/
Dit onderdeel is niet normatief.
Encrypt connections using at least TLS v1.3. Use TLS v1.2 as a fall-back option only. In case of access restrictions use two-way TLD. Since the connection is always encrypted, the authentication method is straightforward. This allows the application of basic authentication tokens instead of encrypted authentication tokens.
Preferrably, APIs should require at least a sign-up process that involves accepting its fair use policy before an API key is issued.
There is an inherent security issue when passing tokens as a query parameter, because most Web servers store query parameters in the server logs.
In the case of APIs that have access-restrictions or purpose-limitations, additional authentication based on PKIoverheid certificates and mutual TLS authentication should be provided.
Using the Warning
response header in all responses of the deprecated APIs,
users are informed of the deprecation and upcoming removal date.
APIs receive and send JSON.
APIs may support JSON Schema (<http: json-schema.org)="">to allow and facilitate validation.
Besides JSON, APIs should support other representations as XML and RDF using the
default HTTP content negotiation mechanism. In case the requested format cannot
be provided, a 406 ot Acceptable
response is sent.
Check the Content-Type
header is application/json
or another supported
content types, otherwise send the HTTP status code 415 Unsupported Media Type
.
Define field names in a such a way that the first word starts with a lower case and subsequent words start with a capital letter, with no intervening spaces or punctiation.
The assumption is that REST clients and Web browsers (either with or without add-ons or extensions) can pretty print JSON.
By default, don't apply an envelope.
POST
, PUT
, and PATCH
payloadsAPIs support at least JSON-encoded POST
, PUT
, and PATCH
payloads. Encoded
form data (application/x-www-form-urlencoded
) is not supported.
Use uniqe query parameters that correspond to the fields that can be queried.
sorteer
to sortSpecify the comma-separated field to sort using the generic query parameter
sorteer
. Placing a minus sign (-
) in front of a field name, the field is
sorted in descending order.
zoek
for full-text searchAPIs support full-text searching using the query parameter zoek
.
*
and ?
wildcard characters for full-text search APIsFull-text search APIs should support two wildcard characters:
*
Matches zero or more (non-space) characters
?
Matches exactly one (non-space) character
Preferrably, GEO APIs should support the GeoJSON standard (RFC-7946).
In case a JSON (application/json
) response contains a geometry, represent it
in the same way as the geometry
object of GeoJSON
(RFC-7946):
{
"type": "Point",
"coordinates": [125.6, 10.1]
}
POST
endpoint for GEO queriesSpatial queries are sent in a POST
to a dedicated endpoint.
POST
endpointsMixed queries may include both spatial and property queries.
In case of a global query /api/v1/_zoek
, results should be placed in the
relevant geometric context, because results from different collections are
retrieved. Express the name of the collection to which the results belongs in
the singular form using the property type
.
General usage of the European ETRS89 coordinate reference system (CRS) is preferable, but is not necessarily the default CRS. Hence, the CRS has to be explicitly included in each request.
The coordinate reference system (CRS) for both the request and the response are
passed as part of the request headers and reponse headers. In case this header
is missing, send the HTTP status code 412 Precondition Failed
.
The CRS for the geometry in the response body is defined using the Accept-Crs
header. In case the API does not support the requested CRS, send the HTTP status
code 406 Not Acceptable
.
application/hal+json
for paginationAdd two reserved fields _links
(required) and _embedded
(optional) to the
representation. Pass pagination meta data as HTTP headers.
For caching apply the default HTTP caching mechanisms using a few additional
HTTP headers (ETag
or Last-Modified
) and functionality to determine wether
a few specific HTTP headers are supplied (If-None-Match
or
If-Modified-Since
).
To prevent server overload and to guarantee a high service level, apply rate limiting to API requests.
Use the HTTP header X-Rate-Limit
to inform users of rate limits. In case the
rate limits are exceeded, send the HTTP status code 429 Too Many Requests
.
API support the default error messages of the HTTP 400 and 500 status code ranges, including the parsable JSON representation (RFC-7807).
APIs should at least support the following HTTP status codes: 200, 201, 204, 304, 400, 401, 403, 404, 405, 406, 409, 410, 415, 422, 429, 500, and 503.
In JavaScript, only use restricted API-keys, linked to specific characteristics of the client-application (web application or mobile application), e.g. a clientId and/or referring URL.
Check the domain of the incoming request and generate the response header
depending on whether this domain may send requests or not (whitelist). In that
case, only add this particular domain to the response header
Access-Control-Allow-Origin
.
**NOTE: It is technically possible to pass a wildcard ("*") in the response
header Access-Control-Allow-Origin
to allow all sources. However, this is
malpractice!**