Contribute: # REST API guidelines
Rationale
When writing new REST APIs, there are multiple ways to achieve the same feature.
Robustness principle
Try to follow the Robustness Principle:
Be conservative in what you do, be liberal in what you accept from others
REST API call = desired state, when possible
Example:
# Delete a user
DELETE /user/12
> 204
# Delete the same user again
DELETE /user/12
> 204, not 404
Generalization: do not raise an error if the desired state of a resource is already attained.
PUT response = no body
When receiving a PUT query, do not send the resource back in response.
Pros:
- less bandwidth
- still available in edit event and query body
- generally unused
Cons:
- body may be useful
- resource may be deleted right after the PUT and won't be available by GET
What is in GET /status?
Each component in a daemon must be able to add values inside the body of /status
.
Example body
{
"ari": {
"status": "ok"
},
"bus_consumer": {
"status": "ok"
},
"service_token": {
"status": "ok"
},
"plugins": {
"voicemails": {
"cache_items": 12,
"status": "ok"
},
"bad_plugin": {
"status": "fail"
}
}
}
Conventions
-
Each key MAY have a
status
subkey. Each value associated to thestatus
subkey MUST contain a string. Conventional values for this string areok
andfail
. Other values are acceptable too, depending on the status being reported. -
If the key does not have a
status
value, the status SHOULD be consideredok
. -
Key names must use
_
(underscore) as word separators.
Target audience
GET /status
is intended for an administrator of the Wazo Platform instance. It MAY include technical details that MUST NOT be available to users or tenant-restricted admins.
REST API status codes
Invalid token = 401 Invalid tenant for the token = 401 Insufficient ACL = 403
Errors in REST API
Rationale
Errors should be as informative as possible when presented to the user. Hence, the REST API must give as much information as possible, and this information must be structured. A simple error message is not enough: it can't be translated easily, it does tell a GUI what field caused the error, etc.
Error structure
When an error occurs during a HTTP request on a REST API, the error returned must be in JSON format, with the following structure:
{
"error_id": "not-found", # this field serves as a "sub-status-code", e.g. to distinguish between two different 400 status codes that don't have the same cause
"message": "No such user ID: 'abcdefg-1234'", # this should be human-readable
"resource": "user", # this helps abstracting the client-side logic, giving back the context of the request
"resource_id": "abcdefg-1234", # idem. This field is optional. The value may be a dictionary.
"timestamp": 1500908147.0837858, # the timestamp when the error occured
"details": {
# as many details as we want about this error, usually what field is invalid and why
}
}
Error details structure for invalid request
When returning a 400 status code, the error details should have this structure:
{
...
"details": {
"field1": {
"constraint_id": "regex",
"constraint": "[a-z]+",
"message": "field1 must be lowercase alphabetic"
},
"field2": {
...
}
}
}
In case of a nested request, the details should be nested as well. Example:
Request:
{
"config": {
"url": "invalid"
}
}
Response:
"error_id": "invalid-data",
"message": "Invalid request",
"resource": "subscription",
"resource_id": "abcdefg-1234",
"timestamp": 1500908147.0837858,
"details": {
"config": {
"url": {
"constraing_id": "url",
"constraint": {
"schemes": ["http", "https"]
},
"message": "Invalid URL"
}
}
}
Pagination and collection in REST API
Parameters
direction
: Sort list of items in 'asc' (ascending) or 'desc' (descending) order.limit
: Maximum number of items to return in the list.offset
: Number of items to skip over in the list.order
: Name of the field to use for sorting the list of items returned.recurse
: Should the query include sub-tenants.search
: Search term for filtering a list of items. Only items with a field containing the search term will be returned.
Collection result
items
: Result of objects filteredfiltered
: Number of object filtered by the filtering keys except fromlimit
andoffset
total
: Number total of objects without filters
{
...
"items": [
{
"uuid": "00000000-0000-0000-0000-aaaaaaaaaaaa",
"name": "My object"
},
{
"uuid": "00000000-0000-0000-0000-bbbbbbbbbbbb",
"name": "Other object"
}
],
"filtered": 2,
"total": 4
}
}
OpenAPI convention in Rest API
Endpoints parameters order
- All type
HEADER
- All type
PATH
- All type
BODY