REST / API best practices and functionality recommendations

Notes from designing a beautiful REST&JSON API.

Resources

  • Resources are nouns not verbs (eg accounts not creating accounting)
  • Resources are coarse grained not fine grained - resources need to encapsulate a lot
  • Your main resources should nearly always be plural

Two types of Resource

  • Collection (e.g /accounts)
  • Instance resources (e.g /accounts/123)

Behaviours

Behaviours

  • GET read
  • POST Create
  • PUT Update (unique identifier is known by client)
  • DELETE delete
  • HEAD get header information, used for cross origin resource sharing, authentication checking
  • PATCH

Media Types

Media Types are set in your request header and response header tells you what the server is giving you.

  • Format specification + parsing rules
  • Request: Accept header
  • Response: Content-Type header

e.g:

  • application/json
  • application/foo+json
  • */*

Design

https://api.example.com rather than https://www.examle.com/service/api/public/rest

Versioning

In the URL https://api.example.com/v1 or in the header request: application/json;application&v=1

Camelcase

Use CamelCase not under_scores!

Date/Time/TimeStamp

All resources should have createdAt field. Use ISO8601 standard, and use UTC so you don't have to worry about the local timezones. Use Carbon to manage date functions.

 Headers

Header values are comma delimited in order preference:

Accept: application/json, text/plain

Resource extensions

Adding an extension will override the Accept header

/application/abc123.json /application/abc123.csv /application/abc123.txt

Error codes

415 - Unsupported media type

more

Instance resources with htef

Always return a href to the resource

{
    href: 'https://api.example.com/v1/account/123',
    name: 'joe blogs'
    email: 'joe@example.com'
}

Collection references

GET /accounts

 Instance reference

GET /accounts/123

Resource expnsion

GET /accounts/123?expand=directory

Pagination

Collection resources should support parameters

  • Offset
  • Limit
  • Size
  • e.g /accounts?offset=50&limit=25

and return first,next,last urls:

{
    "href": 'https://api.example.com/v1/account/123',
    "offset": 50,
    "limit": 25,
    "size": 250,
    "first" : {
                    "href": 'https://api.example.com/v1/account/123?offset=0&limit=25'
    },
    "next" : {
                    "href": 'https://api.example.com/v1/account/123?offset=75&limit=25'
    },
    "last" : {
                    "href": '...'
    },
    "items": [
        {
            name: 'joe blogs'
            email: 'joe@example.com'
        }
    ]
}

Many to Many

  • A group can have many accounts
  • An account can have many groups

Use href links to account and group details:

"items": [
        {
            name: 'joe blogs'
            email: 'joe@example.com'
            account: {
                "href": 'https://api.example.com/v1/account/123'
            }
        }
    ]

Errors

  • Be as descriptive and give as much information as possible

    { 
    "status": 409,
    "code": 12345,
    "property": ""
    "message": ""
    "developerMessage" : ""
    "moreInfo": ""
    }
  • status is HTTP status
  • code is your internal code
  • message is a standard field and should always be included

Security

  • Avoid sessions
  • Authorise based on resource, not url
  • Use API keys instead of username/password
  • Use existing protocols
  • 401 (unauthorised) means unauthenticated
  • 403 (forbidden) means unauthorised

IDs

  • IDs should be opaque and unique
  • Avoid auto increment IDs, use UUID and uniqid() but be careful as it's not secure. Auto increments could allow someone to work out how big your database is and browse different endpoints by just changing the key.

Method Overrides

If you want users to use a FORM to interact with your API rather than Curl etc, attach the method at the end of the form URL.

For example:

POST /accounts/xyz123?_method=DELETE

(Laravel does this)

Tools