- Conventions
Created by gh-md-toc
Keywords have been adopted from RFC conventions to signify requirements.
Current Maintainer is joyce.stack@mendeley.com
Original documented drafted and attributed to Matt Thomson in 2014.
This document borrowed heavily from:
Don’t use RFC2616.
RFC2616 was the reference for HTTP, but now it’s deprecated. You’ll still see it referred to in lots of places.
Source: mnot’s blog
Reworked into these six RFCs:
- RFC7230 - HTTP/1.1: Message Syntax and Routing
- low-level message parsing and connection management
- RFC7231 - HTTP/1.1: Semantics and Content
- methods, status codes and headers
- RFC7232 - HTTP/1.1: Conditional Requests
- e.g., If-Modified-Since RFC7233 - HTTP/1.1: Range Requests - getting partial content
- RFC7234 - HTTP/1.1: Caching
- browser and intermediary caches
- RFC7235 - HTTP/1.1: Authentication
- a framework for HTTP authentication
- RFC5988 - HTTP/1.1: Web Linking
https://tools.ietf.org/html/rfc5988 - Web Linking
coming shortly
Golden Rule
Once we've released an API, we must NOT make breaking changes to it.
The Golden Rule MUST NOT be broken apart from in exceptional circumstances, such as for legal reasons.
The versioning stategy has changed, existing endpoints using the 'legacy' versioning will continue to be supported until deprecated.
All new endpoints MUST have a version number in their URL directly beneath the resource name.
e.g. /documents/v1/<id>
Endpoints that include a version within the URL MUST NOT include a version within the media type.
application/vnd.mendeley-<RESOURCE_TYPE>+json
e.g. application/vnd.mendeley-document+json
Clients MUST send a non-null and non-wildcard Accept header for endpoints that produce content. Clients MUST also send a non-null Content-Type header for endpoints that consume content.
Use HTTP's content negotiation mechanism for versioning. Custom media types are used in the API to let the consumers choose the format of the data they wish to receive. This is done by adding the Accept
header when you make a request. Media types are specific to resources, allowing them to change independently.
/<RESOURCE_TYPE>s/<id>
application/vnd.mendeley-<RESOURCE_TYPE>.<VERSION>+json
e.g. /documents/<id>
application/vnd.mendeley-document.1+json. The "1" here is the version number of the representation.
Both client and servers are responsible for negotiation to be successful.
Client Responsibilities
- Clients MUST send an appropriate resource Media type in the header * e.g. Accept: application/vnd.mendeley-document.1+json
Server Responsibilities
- Servers MUST respond with a Content-Type header * e.g. Content-Type: application/vnd.mendeley-document.1+json
Endpoints that include a version within the media type MUST NOT include a version within the URL.
All endpoints MUST follow the Release Lifecycle, each phase of the lifecycle has a set of rules associated with it.
During this phase of development endpoints may change without giving notice to internal/external clients. Properties can be added and removed, media types modified and the entire endpoint renamed or removed.
When an endpoint is promoted to the Beta phase clients using the endpoint MUST be notified of breaking changes and reasonable notice given to allow development teams to respond to the proposed changes; non-breaking changes can be made at any time without notice.
Breaking changes MUST NOT be made to Released endpoints; non-breaking changes can be made at any time without notice. If breaking changes are required (for example, to remove an unused property) then a new version of the endpoint must be developed.
When a new version of an endpoint is promoted to the Released stage the previous version will be moved to the Deprecated stage. Client teams using the endpoint MUST be informed immediately and the deprecation date MUST give reasonable time for migration to new endpoints. Client teams still using the endpoint SHOULD be reminded of the retirement before the deprecation date.
Retired endpoints, those with a deprecation date in the past, will not be available - the client MUST receive either a 410 or 404 status code if the endpoint is requested after this time.
ISO 8601 format MUST be used for all dates and times e.g. 2015-02-18T04:57:56Z
! Unfortunately, some standard HTTP headers use their own format, defined in RFC 2822: Thu, 01 May 2014 10:07:28 GMT
Server-generated dates MUST be used everywhere as the server is the only reliable clock.
Collections of resources can be operated on using HTTP verbs.
GET
- Fetch a representation of the resource at the URI.
POST
- Store the enclosed entity as a subresource of the resource at the URI.
PUT
- Store the enclosed entity under the URI.
DELETE
- Delete the resource at the URI.
PATCH
- Apply partial modifications to the resource at the URI.
Naming
Resources MUST be identified using plural nouns. A single resource MUST be identified using a single noun.
* /things - identifies a collection of resources.
* /things/(resource_identifier) - identifies a single resource within a collection
Namespaces
Each collection of URI templates MUST include a namespace at the start of the URI. This namespace identifies a collection of related resources. This avoids collisions when there is multiple resources with similar names but different functionality.
URI Templates
URI templates MUST follow either the URI template
/{namespace}/{resource}/{resource_identifier}/{sub_resource}/{sub_resource_identifier}/
HTTP Cheat Sheet
GET | POST | PUT | DELETE | PATCH | |
---|---|---|---|---|---|
How does the HTTP spec define it? | The GET method requests transfer of a current selected representation for the target resource. | The POST method requests that the target resource process the representation enclosed in the request according to the resource’s own specific semantics. | The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. | The DELETE method requests that the origin server remove the association between the target resource and its current functionality. | The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI. (RFC 5789) |
Is it safe? (no side effects) | Yes | No | No | No | No |
Is it idempotent? (repeating the request may give a different response, but makes no material difference to the resource) | Yes | No | Yes | Yes | No |
Are responses cacheable? | Yes | Only when explicitly marked as such | No | No | Only when explicitly marked as such |
Can requests include a body? | No | Yes | Yes | No | Yes |
What could it be used for in a RESTful API? | Fetching a representation of the resource | Creating a new resource from a representation | Replacing a resource with a representation | Deleting a resource | Applying partial updates to a resource |
All resources SHOULD provide a filtering mechanism where it is expected to return a large collections.
Query parameters MUST be used to filter on resources.
* e.g. /dogs?type=poodle
Some collections are too large to return in one response e.g. catalog, institutions or location information.
In such cases the default SHOULD default to returning a limited number of results or enforcing a CAP. What is the 'default' is specific to the resource in question e.g. most popular locations/institutions.
Mandatory query parameters MAY also be used.
Large collection resources MUST have an upper bound on the number of items in the response. It is not desirable for either our servers or our clients for us to dump all the data in one go.
If a client requires a mechanism to iterate over a collection of resources then cursor-based pagination MUST be used.
Link headers are used to link to other pages. Using Link headers keeps resource representations clean.
GET /documents
200 OK
Link: </documents?marker=291d3064-4f74-4932-bfc8-4277d441705b>; rel="next";
[
]
// do
limit
MUST be used to indicate the upper bounded value.
modified_since or deleted_since or {property_name}_since SHOULD be provided if time selection is needed.
Developers SHOULD NOT write bulk APIs.
e.g. GET /dogs/id1,id2,id3,
Why?
- different response formats from the same URI
GET /dog/id1
should return a single resource and not a collection of resources. - breaks cacheability
- URL doesn’t represent a resource
- URLs have a maximum length
Creates a new resource using the POST verb. The response to a POST MUST be 201 Created
, with a Location header
containing the URL where the resource can be found. The body MUST contain a representation of the resources (including any server-generated fields).
URI template
POST /{namespace}/{resource}/
Example request and response POST /datasets/drafts {"field": "value"}
201 Created
Location: https://api.mendeley.com/datasets/drafts/291d3064-4f74-4932-bfc8-4277d441705b
{"field": "value", "created": "2015-02-18T04:57:56Z"}
Clients MAY use the HTTP Prefer header to give a client a mechanism to return a full representation or not:
Prefer: return=minimal
Prefer: return=representation
The body of a POST
request, and the body returned on a GET request to the URL from the Location
header, should have the similar structures.
POST /things {"field": "value"}
201 Created
Location: https://api.mendeley.com/things/291d3064-4f74-4932-bfc8-4277d441705b
GET /things/291d3064-4f74-4932-bfc8-4277d441705b
200 OK
{"field": "value", "created": "2015-02-18T04:57:56Z"}
GET response will usually have more fields, but that’s OK. There are some some cases where this doesn’t work e.g.
- POST /files: body is the file bytes
- GET /files/(id): body is the file bytes
- extra metadata (e.g. the document that the file is attached to) goes in the headers as it’s the only place left
Clients MAY using the post once exactly pattern to avoid duplicates being created.
First Attempt works OK:
POST /poe/documents/291d3064-4f74-4932-bfc8-4277d441705b
{"title": "Underwater basket weaving"}
200 OK
Location: /documents/7ab2c167-8e48-4fb8-85b0-73cdb8662a64
Second Attempt works errors:
POST /poe/documents/291d3064-4f74-4932-bfc8-4277d441705b
{"title": "Underwater basket weaving"}
405 Method Not Allowed
Allow: GET
This is just a draft and has not been updated in 10+ years but is a suggested pattern.
You MAY use LINK and UNLINK hypermedia links to indicate when one resource is linked/unlinked to another.
Example request
If you had to create a 'Dog' resource and link it to an 'Owner' then you could do:
POST /dogs
Link: </owners/291d3064-4f74-4932-bfc8-4277d441705b>; rel="owner";
Example in pagination This shows the link to the 'next' item in the list.
GET /documents
200 OK
Link: </documents?marker=291d3064-4f74-4932-bfc8-4277d441705b>; rel="next";
[
]
// do
You MAY use the UNLINK method to unlink one resource from another.
Specified by this internet draft, but at the time of writing is not an approved RFC.
Reads a single resource using the GET verb from a collection of resources.
GET calls can be called multiple times without any side effects i.e. idempotent.
The response to a GET MUST be 200 OK
. The body MUST contain a representation of the resources including any server-generated fields. In the event the resource is not located then the HTTP status returned MUST be 404 Not Found
.
URI template
GET /{namespace}/{resource}/{resource_identifier}
Example request and response
GET /datasets/articles/{id}
{
"id": "53c9523b-7535-4501-9969-93706f14672a",
"title": "Distinct loci of lexical and semantic access deficits in aphasia: Evidence from voxel-based lesion-symptom mapping and diffusion tensor imaging",
"doi": "10.1016/j.cortex.2015.03.004",
"journal_id": "7be5c683-a7c2-4fe3-95c8-947b58706f2b"
}
Updates a single resource using the PATCH verb. Applies a partial update.
The response to a PATCH MUST be 200 OK
. The body MUST contain a representation of the resources including any updated server-generated fields. In the event the resource is not located then the HTTP status returned MUST be 404 Not Found
. If the patch is invalid then a 422 Unprocessable Entity
should be returned.
If applying the PATCH puts the resources in an invalid state then a 409 Conflict
should be returned.
Content-Type must be set to the resource's custom media type eg. application/vnd.mendeley-draft-dataset.1+json
URI template
PATCH /{namespace}/{resource}/{resource_identifier}
Example request
PATCH /datasets/drafts/{id}
{
"name": "Testing One Two"
}
Beware of cases where PATCH /resource1
can affect the state of /resource2
.
Clients MAY use a precondition check of If-Unmodified-Since
header on update requests. If specified, the resource in question will not be updated if there have been any other changes since the timestamp provided. Should be specified in RFC 2822 format e.g. Thu, 01 May 2014 10:07:28 GMT
It MAY be a requirement and the server can send back 428 Precondition Required
if there is no If-Unmodified-Since
header. A HTTP status of 412 Precondition Failed
MUST be returned if the update fails.
This MAY be used for GET requests e.g don’t download data that hasn’t changed since the client last requested it
Removes properties of a single resource using the PATCH verb and a subset (remove operation) of JSON PATCH semantics.
Content-Type must be set to application/json-patch+json
Example request
PATCH /datasets/drafts/{id}
[
{ "op": "remove", "path": "/name" }
]
The "remove" operation removes (sets to null) the value at the target location.
The target location MUST exist for the operation to be successful.
Deletes a single resource using the DELETE verb. The response to a DELETE MUST be 204 No Content
. In the event the resource is not located then the HTTP status returned MUST be 404 Not Found
. The action may be forbidden by a particular client so a HTTP status of 403 Forbidden
MUST be returned.
URI template
DELETE /{namespace}/{resource}/{resource_identifier}
Example request and response
DELETE /datasets/drafts/{id}
204 no content
Sometimes it maybe necessary to nest resources because a sub resource can't exist without a parent resource. Developers are encouraged to think about if resources need to be nested and if they could be identified by a single ID.
URLS MUST not exceed two levels of nesting.
All standards as outlined in the Resources section should be adhered to.
Problems with multiple identifiers
Maintaining multiple IDs can make extra work for clients having to maintain the relationships between parent and child resources.
Overhead on server developers who have to validate multiple IDs.
URI template
VERB /{namespace}/{resource}/{resource_identifier}/{sub_resource}/{sub_resource_identifier}
Example request
GET /submissions/fb5cd024-fb53-3366-b3f2-0dd6910cb73e/attachments/60721cc1-c1f3-678c-4235-f239d6415df1
Bad example
GET /library/documents/7135271181/file/3221525ea6f746b577b6a8ad40d89df3f41f776a/2589311
In this example we have no context what the IDs are identifying.
Some actions don’t naturally map to a HTTP verb. Verbs such as 'activate', 'cancel', 'validate', 'accept', 'reset', 'verify', and 'deny' are examples. These are usually a procedural concept.
In this case you MUST use a POST with a verb. The word action
MUST be the last segment of the URI. The action
MUST be used in the body of the POST. This allows us to version the content in the cases where no request body is provided e.g. resending verification email.
The request Content Type MUST be application/vnd.mendeley-{resourcetype}-action.1+json
.
Developers are encouraged to consider resource design alternatives over using this approach. This should be used infrequently as its an anti-pattern.
Risks (I've taken these from Pay Pals standards as I think they apply here too.)
- The URI can't be extended to include a sub-resource beyond the verb.
- There is no corresponding read actions so this can be difficult to test.
- Retrieving a history or audit of the call will have to live in another resource
e.g. send_password_reset_email_history
URI template for action on a single resource
POST /{namespace}/{resource}/{resource_id}/actions
Example request
POST /datasets/drafts/bnwvgpfvhf/actions
{
"action": "send_password_reset_email"
// any other additional information
}
Before considering making an action
of an import action then consider:
* What resource are you importing?
* Should you consider reusing an existing resource with a different Content Type?
* Look at the attributes you are using to POST to the import - are they similar to attributes in another resource? * Is their a natural fit?