No REST for the whippet.
Guidelines for designing RESTful APIs, with dogs.
Hello! I'm Tom Spencer (@fiznool)
When I'm not racing F1 cars,
I'm a Full-Stack Web Developer,
most recently building APIs with Node.js.
Why build an API?
Provide data access to a wide number of consumers.
What is REST?
REpresentational State Transfer
Very strict set of rules, governing client-server communication
Use as a guide, not a bible
Don't be a RESTifarian...
Building an API is mostly a design problem.
Design or study data model
Try to represent each model as a resource
Design URLs to access and manipulate resource (CRUD)
Aim for two URLs per resource.
For a data model 'dog':
/dogs
/dogs/{id}
Prefer plural form 'dogs' over singular 'dog'
Requests: Use HTTP Methods.
GET
POST
PUT
DELETE
Retrieve
Create
Update
Delete
Resource
/dogs
/dogs/1234
Create a new dog
List (index) dogs
Batch update dogs
(Delete all dogs)
Retrieve a dog
Update a dog
Delete a dog
(error)
URLs: verbs are bad,
nouns are good.
Bad
Good
/api/getAllDogs
/api/getDogById?id=1234
GET /api/dogs
GET /api/dogs/1234
/api/createDog
POST /api/dogs
What about responses?
Everything's OK
Three types of response:
Client did something wrong
Server did something wrong
Use HTTP Status Codes.
2xx
4xx
5xx
The "everything's OK" codes.
200 (OK)
Code
Use when
Request was handled successfully and no other 2xx codes apply.
201 (Created)
Request to create a resource was successful.
Example
GET /dogs
POST /dogs
204 (No Content)
Request was handled successfully, and no data was returned.
DELETE /dogs/1234
The "something's wrong" codes.
400 (Bad Request)
Code
Use when
Passed data was invalid.
401 (Unauthorised)
Client needs to supply credentials to access resource.
Example
POST /dogs
Resource does not exist.
GET /dogs/9876
GET /dogs
404 (Not Found)
Server threw an exception.
POST /dogs
500 (Server Error)
Make friends with JSON.
[{
"id": 1234,
"name": "Benji"
}, {
"id": 5678,
"name": "Fenton"
}]
GET /dogs
(XML is icky).
Time for some examples!
Retrieve
Retrieve Dogs
< 200 OK
[{
"id": 1234,
"name": "Heinz"
}, {
"id": 5678,
"name": "Fenton"
}]
Get all dogs
> GET /dogs
Retrieve Dogs
< 200 OK
{
"id": 1234,
"name": "Heinz"
}
Get dog by ID
> GET /dogs/1234
Retrieve Dogs
Dog not found
< 404 Not Found
> GET /dogs/4321
Filter Dogs
< 200 OK
{
"id": 1234,
"name": "Heinz"
}
Get all dogs dressed in a costume
> GET /dogs?dress=costume
Paginate Dogs
Get dogs 1-2
< 200 OK
[{
"id": 1234,
"name": "Heinz"
}, {
"id": 5678,
"name": "Fenton"
}]
> GET /dogs?offset=0&limit=2
Paginate Dogs
Get dogs 3-10
< 200 OK
[]
> GET /dogs?offset=2&limit=8
Sort Dogs
Get dogs, alphabetical
< 200 OK
[ {
"id": 5678,
"name": "Fenton"
}, {
"id": 1234,
"name": "Heinz"
}]
> GET /dogs?sort=name
Sort Dogs
Get dogs, reverse alphabetical
< 200 OK
[{
"id": 1234,
"name": "Heinz"
}, {
"id": 5678,
"name": "Fenton"
}]
Notice the - to sort in descending order
> GET /dogs?sort=-name
Create
Create a Dog
Create a new dog
< 201 Created
{
"id": 9010,
"name": "Santa's Little Helper"
}
> POST /dogs
{
"name": "Santa's Little Helper"
}
Create a Dog
Validation Error
< 400 Bad Request
{
"errors": [{
"field": "name",
"type": "required",
"message": "Name is required"
}]
}
> POST /dogs
{
"name": ""
}
Update
Update a Dog
Update an existing dog
< 200 OK
{
"id": 1234,
"name": "Hot Dog"
}
> PUT /dogs/1234
{
"name": "Hot Dog"
}
Update a Dog
Batch update dogs
> PUT /dogs
[{
"id": 1234,
"name": "Hot Dog"
}, {
"id": 5678,
"name": "Fentonnn!"
}]
< 200 OK
[{
"id": 1234,
"name": "Hot Dog"
}, {
"id": 5678,
"name": "Fentonnn!"
}]
Update a Dog
Validation Error
> PUT /dogs/1234
{
"name": ""
}
< 400 Bad Request
{
"errors": [{
"field": "name",
"type": "required",
"message": "Name is required"
}]
}
Update a Dog
Dog not found
< 404 Not Found
> PUT /dogs/4321
{
"name": "Hot Dog"
}
Delete
Delete a Dog
Delete an existing dog
< 204 No Content
> DELETE /dogs/1234
Delete a Dog
Dog not found
< 404 Not Found
> DELETE /dogs/4321
Other Actions
Other Actions
Some actions can't be represented RESTfully.
For example, how to tell server to walk the dog?
(Yes, this server is smart.)
Break the rules - use a verb.
Dog Actions
Walk the dog
< 200 OK
{
"id": 5678,
"name": "Fenton",
"status": "walking",
"walkies": "youtube.com/embed/bmpONxJ7JSw?t=15s"
}
> POST /dogs/5678/walkies
Dog Actions
Walk the dog
Productisation
Preparing your API for the outside world
Authentication
Go Stateless: send a token with every request
Two suggestions:
API Key
OAuth 2.0
Return 401 (Unauthorised) if token not valid
> GET /dogs
X-Access-Token: 1234321
API Key: Send token as a header
Authentication
Simple
API consumer must acquire token manually
Identify individual users
Option 2: Use OAuth 2.0
Authentication
Fully automated token exchange
Complex: you need to build/integrate an OAuth server
Identify individual users
Prevent consumers from tying up your server
Rate Limiting
The dreaded DoS!
Ties into authentication: track number of requests per user for a fixed period
Prevent access if limit reached (use status code 429: Too Many Requests)
They will happen...
Errors
If validation error, send as much info to the client as feasible.
< 400 Bad Request
{
"errors": [{
"field": "name",
"type": "required",
"message": "Name is required"
}, {
"field": "breed",
"type": "enum",
"message": "Breed must be one of
Corgi, Labrador"
}]
}
field
Name of field that was invalid
type
Type of validation error that was triggered
message
Error message to be displayed
Errors
If server error, send a general failure message.
< 500 Internal Error
{
"message": "There was a problem
processing your request."
}
Since the API is a black box, it may also be useful to provide a 'developer' message, which gives instructions to the developer about what happened.
Documentation
Last, but certainly not least!
What happens if you don't provide good documentation?
Documentation
It will take time, but it is essential.
Tools:
Inspiration:
Code Tutorial
A Test-Driven tutorial to build an API using the guiding principles of this slide deck.
Further Reading
Any Questions?
No REST for the whippet
By Tom Spencer
No REST for the whippet
- 6,161