Introduction
The JavaScript Object Notation (JSON) format is a widely adopted standard to deliver HTTP RESTful API responses. This mostly because of the following properties:
- It is a standard open lightweight data-interchange format;
- Along with XML is the main format for data interchange used on the modern web;
- It is easy for humans to read and write;
- It is easy for machines to parse and generate;
- Supports all the basic data types (numbers, strings, boolean, arrays and hashes);
- It is developer-friendly, as it can be generated and parsed from almost any programming languages;
- Popular databases (e.g. MongoDB, MySQL, PostgreSQL) can store the JSON format natively.
Following a shared convention promotes reuse and helps increasing the productivity by leaving more time to focus on the actual development task. However, while there are many common patterns for structuring JSON-based HTTP/REST API responses, there is no consistency in things like naming or types of responses.
Several models have been proposed, but none has emerged as clear standard (e.g. JSend, JSON API, OData JSON Protocol, HAL, HATEOAS, etc…). Some models are quite simple, while others tends to be complex and over-prescribing. Amongst them the JSend format is probably the simplest one, defining only four top level fields (i.e. status, code, message and data). The simplicity of this model and its focus on application-level messaging are important features to promote adoption and reuse.
Extending the JSend model
In addition to the fields defined by JSend, the model proposed here defines new mandatory fields that are particularly useful in modern distributed environments.
Every response should be a well-formatted JSON, always including the following fields:
program
: Program name - helps to identify that the response is coming from the right application;version
: Program version - in a distributed environment the response may come from a different version of the same program;release
: Program release number - useful for debugging purposes;datetime
: Human-readable UTC date and time of when the response has been dispatched;timestamp
: Machine-readable UTC timestamp in nanoseconds since EPOCH of when the response has been dispatched;status
: Status code (i.e. success, fail or error);code
: HTTP status code or a numeric code corresponding to the error in an error result;message
: A meaningful, end-user-readable (or at the least log-worthy) message, explaining the current status (e.g. what went wrong in an error result);data
: Data payload - a generic container for data returned for success, fail or error.
If the responses are stored as logs for auditing or debugging purposes, the new additional fields provide the required what and when information.
The API service should always return the above JSON object, even in case of error or panic. This allows to use a consistent parsing method.
Default API entry points
The API service should always provide two standard GET entry points:
1. Index “/
”
Return a list of all available entry points (routes). For example:
{
"program": "myprog",
"version": "1.2.3",
"release": "45",
"datetime": "2016-10-06T19:58:29Z",
"timestamp": 1475783909566791977,
"status": "success",
"code": 200,
"message": "OK",
"data": {
"routes": [
{
"method": "GET",
"path": "/status",
"description": "check this service status"
},
{
"method": "GET",
"path": "/password",
"description": "returns a random passwords"
}
]
}
}
2. Status “/status
”
Return the status of the service (i.e. health check). For example:
{
"program": "myprog",
"version": "1.2.3",
"release": "45",
"datetime": "2016-10-06T19:55:10Z",
"timestamp": 1475783710372391716,
"status": "success",
"code": 200,
"message": "OK",
"data": {
"duration": 33.263465257,
"message": "The service is healthy"
}
}