In this chapter we will move beyond the basics of API design and cover some of the more advanced and powerful features.
How to make your API more 'RESTafarian'
Design principles
- Extended HTTP methods
- Error handling
- Searching, sorting and pagination
- Checking for content changes
- Multiple Data Formats
- Authentication
- Logging
- HATEOAS
- Documenting an API
HTTP Methods are the verbs that act on collections and elements. CRUD operations require 4 methods.
GET POST PUT DELETE
- OPTIONS
- HEAD
- PATCH
API consumers see the API as a black box Each response needs to include a status code
401 UNAUTHORISED
Include a human-readable message in the body
{
message: "Basic Authentication required"
}
### Searching a Collection
Search should be considered for collections
Search query passed as a parameter
/books?q=javascript
### Partial Responses
Allows you to supply only the information needed
Reduces the bandwidth
/books?fields=title,author /books/1449336361?fields=title,author
Avoid returning the entire collection!
Default limit should be decided (first 20 records?)
Allow developers to specify fewer records
Also need to specify which block to return.
/books?limit=15&offset=45
If the content has not changed since the last time the resource was requested there should be an option to flag this and to offer the option to not download it again. There are two ways to achieve this.
GET response should include Last-Modified
header
GET request should include If-Modified-Since
header
If match the server responds with 304 NOT MODIFIED
and the headers (no response body)
If content has changed the server responds with 200 OK
and requested resource.
GET response should include ETag
header, this is a hashed string.
GET request should include If-None-Match
header
If match the server responds with 304 NOT MODIFIED
and the headers (no response body)
If content has changed the server responds with 200 OK
and requested resource.
Last-Modified / If-Modified-Since
Uses the RFC 2822 data format
Wed, 21 Oct 2015 07:28:00 GMT
ETag / If-None-Match
Hash of the response body
686897696a7c876b7e
Use the etag module
const etag = require('etag')
res.setHeader('ETag', etag(body))
Const date = new Date('Tue, 27 Dec 2011 02:12:35 GMT')
Const dateStr = date.toISOString()
Representations
We have already split our application into resources
Resources are your idea of how to divide up the data and present it
The resource is the useful information being presented
The same resource may have several representations
For example a book resource:
- A representation containing metadata
- A representation containing the book contents
Works in both directions...
Deciding between representations
Two ways to define this:
Unique URI for each representation
Content negotiation
Information sent in the request header
If multiple formats are available client needs to specify
Two options:
- Accept header
Accept: application/json
- Type format in the URL
/books/1449336361.json
No authentication status stored in API
Each request must provide the authentication needed
- Basic Access Authentication
- OAuth 2.0
Client includes an Authorization header
Username and password combined with a colon
testuser:p455w0rd
This string is encoded using the RFC2045-MIME variant of base64
Printf testuser:p455w0rd | base64
dGVzdHVzZXI6cDQ1NXcwcmQ=
The result is sent in the header prepended with Basic
Authorization: Basic dGVzdHVzZXI6cDQ1NXcwcmQ=
Need to keep logs of all API requests:
- Why is this important?
- What useful data can be found in the request?
Lack of standard responses.
- Most APIs have their own format, typically a JSON response that maps neatly to their own data model.
- Results in lots of APIs that do stuff their own way == confusion
Lack of Hyperlinks
- JSON has no built-in support for hyperlinks, which are a fundamental building block on the Web (W3C).
- API endpoints are linked together only through documentation.
- Forces developers to keep detailed, updated documentation
- Forces developers to read and understand it before using the API.
Hypertext As The Engine Of Application State
Breaks down the principle elements of REST into three steps
Each step progresses towards the ideal implementation of REST
Developed by Leonard Richardson
Level 0
- HTTP as a transport system without web mechanisms
Level 1 – Exposes Resources
Level 2 – Uses Verbs to Manipulate Resources
- Can perform different actions on the same resource
Level 3 – Provide links to other resources
- Can figure out how to interact with the system
- Hypermedia Application Language (HAL)
- JSON for Linked Documents (JSON-LD)
Open specification describing a generic structure for web resources
Achieves Richardson Maturity Model level 3
Presents a new hypermedia type:
application/hal+json
{
"_links": {
"self": {
"href": "http://ex.com/books/1449336361"
}
},
"id": "1449336361",
"name": "JS.Next: ECMAScript 6"
}
{
"_links": {
"self": {
"href": "http://ex.com/books/1449336361"
}
},
"id": "1449336361",
"name": "JS.Next: ECMAScript 6"
}
"_links": {
"self": {
"href": "http://ex.com/books?page=3"
},
"first": {
"href": "http://ex.com/books"
},
"prev": {
"href": "http://ex.com/books?page=2"
},
"next": {
"href": "http://ex.com/books?page=4"
},
"last": {
"href": "http://ex.com/books?page=133"
}
}
POST /books HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"id": "1449336361",
"name": "JS.Next: ECMAScript 6"
}
HTTP/1.1 201 Created
Content-Type: application/hal+json
Location: http://example.org/api/user/matthew
{
"_links": {
"self": {
"href": "http://ex.com/books/1449336361"
}
},
"id": "1449336361",
"name": "JS.Next: ECMAScript 6"
}
Important to produce clear documentation
Helps your users understand how it works
Use APIDoc
Add inline annotations to source code
npm module apidoc generates the documentation
Example:
$ node_modules/.bin/apidoc -e node_modules/ -o docs/api/
This generates the API documentation and creates the web page in the docs/api/
directory. files in the docs/
directory can be published as a website by GitHub. To enable this you should generate the documentation as shown, commit and push the files to GitHub then edit the repository settings and enable GitHub Pages as shown.
This will display a link to the root of the GitHub pages.
Click on this and you get a page not found error. Append api/
to this url and refresh the page to see the documentation.
There should only be 2 per resource:
Collection (plural)
/books
Resource
/books/1449336361
Summary: Keep verbs out of URLS...