RESTful JSON API Design (PART 1)
PART 1. This is the first part in a series of articles on designing RESTful JSON APIs. This part will be about resources, designing routes and adding behaviour.
Most of my knowledge about RESTful API design I’ve gotten from Stormpath’s CTO, Les Hazlewood, in his Best Practices video from 2012: https://www.youtube.com/watch?v=hdSrT4yjS1g. I really suggest you watch it as it is a good complement to this series. I’ve attempted to summarize some of the points he’s making and present some of my own experiences into an easy to follow guide.
REST doesn’t have a clear cut implementation specification but there are lots of conventions and guidelines that you may follow. The most important point which Les is making in his presentation is that an API should be designed for easy adoption and usage before catering to all ideologies of REST design out there and I completely agree. All these points I’m making in this series should make for a more accessible API design but I invite to discussion on improving the ideas presented here so that we all can benefit from it.
With that said, let’s begin.
RESOURCES
Resources are representations of things, collections and/or objects and they may have relationships with other resources. A resource is what a developer interacts with through the API and may operate on it through some pre defined methods (see the behaviour section). There are three types of resources:
- Collection Resource (ex: /artists)
Interact with many objects - Instance Resource (ex: /artists/:id)
Interact with one object - Relationship Resource (ex: /artists/:id/albums)
Interact with many objects related to another object
A resource should always be a noun and should not reflect behaviour, that is, they should never be verbs. Example of bad resources are:
/addArtist
/verifyArtist
/getAlbum
/getAlbumReleaseDate
Having behaviour in your routes will make it more difficult for a developer to instinctivly know the route to use and with time the number of endpoints will grow as you add more behaviour to your API and will quickly become unmaintainable.
Note on the Relationship Resource
You shouldn’t have to string more than two relationship resources together. For example, these are generally bad:
/artists/:artistId/albums/:albumId/tracks
/artists/:artistId/albums/:albumId/tracks/:trackId
Your routes will become very long and you will need to know more identifiers than you should have to. For example, if you want to interact with the tracks of an album you shouldn’t need to know the artist, or, if you want a particular track you shouldn’t need to know the album (This, of course, assumes that all the tracks have unique identifiers, regardless of album or artist. If this is not the case you will need to have your relationship routes like this). Replace the routes above with these instead:
/albums/:albumId/tracks
/tracks/:trackId
Rule of Thumb
You shouldn’t need to know more than one identifier per route.
BEHAVIOUR
To add behaviour to a resource you should use the HTTP methods: POST, GET, PUT and DELETE (I won’t be talking about HEAD nor PATCH). You can think of them corresponding to CRUD (Create, Read, Update and Delete) but they are not truly one to one but it might be easier to think of them this way. Here are some more in depth explanation of the methods:
- GET
Getting data from a resource. Could be applied to any of the resource types:
GET /albums - Returns a list of all albums
GET /albums/23 - Returns a representation of album 23
GET /artists/111/albums - Returns a list of all albums belonging to artist 111
- DELETE
Deleting a resource. Could be applied to any of the resource types:
DELETE /albums - Will delete all albums
DELETE /albums/23 - Will delete album 23
DELETE /artists/111/albums - Will delete all albums belonging to artist 111
For safety you might not want to expose the multiple delete options as this could be dangerous. Instead you may only want to allow the deletion of an instance if your API is not dependent on deleting objects in bulk.
- PUT
Updating a resource with the supplied request data. All fields of the resource is required in the request because the same PUT request should always return the same response (PUT is idempotent). You can only perform a PUT request on an instance resource:
PUT /albums/23 - Will update album 23 with the request data
- POST
Creating or updating a resource with the supplied request data. All fields are not necessary in these requests unlink PUT. POST can be applied to all resouces but with different behaviour:
Create:
POST /albums - Will create a new album with the request data
POST /artists/111/albums - Will create a new album belonging to artist 111 with the request data
Update:
POST /albums/23 - Will update album 23 with the request data
Note
The difference between the PUT update and the POST update is that PUT requires all fields to be supplied unlike POST. You can think of a POST update as a partial update.
FURTHER BEHAVIOR
For adding further behaviour that doesn’t fit with the CRUD paradigm you may use query strings to perform an action, for example:
GET /albums/23?action=get-cover
POST /albums/23?action=update-cover
Use GET when you want something returned and POST when you want something to happen. These routes won’t be instinctivly familiar to the developer why you need to stick to the action query string key and an easy to understand action value, but otherwise this way of adding more behaviour should be infinitely expandable.
Continued in PART 2: http://materik.tumblr.com/post/99806761591/restful-json-api-design-part-2