Skip to main content

REST API

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 the status subkey MUST contain a string. Conventional values for this string are ok and fail. Other values are acceptable too, depending on the status being reported.

  • If the key does not have a status value, the status SHOULD be considered ok.

  • 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 filtered
  • filtered: Number of object filtered by the filtering keys except from limit and offset
  • 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​

  1. All type HEADER
  2. All type PATH
  3. All type BODY