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