Designing a RESTful API

On November 16, 2011, in REST, by Anuj Gakhar

I have been working on designing/architecting/developing an API recently as part of my job. That has given me the opportunity to read a lot about REST API’s best practices, how things should be done, what are the common mistakes people do and what are the pitfalls you should be looking for. One thing I have learned is that a good API pretty much solely depends on good design. I know that sounds obvious, but I have seen a lot of API’s badly designed, eventually making very hard for the application developers to use/consume the API. In this post, I will try to cover some of the things that I have learned about REST API design.

Defining your Resources

The first and foremost thing you want to think about is your resources. Let’s take an example. One of the things that almost every system has is users and most APIs have some way of modifying/retrieving user objects. If you were to support normal CRUD (Create, Update, Update and Delete) Operations for your users via the API, you would normally think of API methods/functions more or less something like this :-

  • getUsers(offset, limit)
  • getUserByEmail
  • getUserByUsername
  • searchUsers(searchCriteria)
  • saveUser(user)
  • createUser
  • deleteUser

And probably a few more……

However, that is NOT good design (I think…). First of all, there is no consistency in method names. And you can end up with a lot of similar methods for each resource. And for an API consumer, there is more to remember and more to deal with. It would be a lot better if we could define a single resource like below :-

Resource GET POST PUT DELETE
/users List all the users based on some default offset and limit if none specified. Can also specify additional filter criteria here like ?username=:username&email=:email as additional optional parameters Create a new user Throw an error as does not make much sense to update an entire user collection OR you can choose to bulk update users if you wish (personal choice) Delete all users
/users/:username List the username specified Throw an error as does not make much sense to create a new user when already in a user context Update the user in question Delete the user in question

HTTP gives us the methods already – GET, PUT, POST and DELETE. You can read about these method definitions here. So by defining a single resource “/users” and using the HTTP methods, we have pretty much covered all of the previous methods with a single resource. Well, call it 2 if you want. As we also have a “/users/:username” but it’s the same resource, except it drills down to one user.  And because the resource is most likely always going to be more than one (ie you won’t have 1 user in your system or 1 video etc), it kinda makes sense to keep the resource name plural (“/users” instead of “/user”). Now, of course a lot of this (updating, creating, deleting) involves permissions and authorisation as well but that’s not what I am covering here. I am only discussing here how the resources/endpoints should be setup. You can also setup pagination in the above by supporting 2 additional parameters (offset and limit, start and count etc…).

Some Examples :-

A typical searchUsers call would look like :-

[xml]/users?email=someone@mysystem.com[/xml]

To list users on Page 4 , assuming you are doing 25 per page, you could do

[xml]/users?offset=75&limit=25[/xml]

To list top 25 most active users

[xml]/users?limit=25&sortby=mostactive[/xml]

and so on…

You get the drift…..If you think about this, once a developer/consumer reads about this, there is nothing to memorize. It’s pretty easy to grasp and use. So, defining and setting up resources in the right way is very important.

Versioning

This may not seem very critically important to begin with, but if you think about it, it is. As your user base grows and more and more third party developers start using your API, you will no doubt be adding more functionality to your API. Some of it will be new and some will be updates to existing functionality. Or you may simply decide to throw away what you have and refactor the whole thing from scratch. There are many possibilities. The thing is, once the API is exposed to public and people start writing code based around your API, you then have a responsibility to make sure that it does what it’s supposed to do and not start throwing errors when you update something or add something new. You can not afford to add new functionality and break existing functionality. Now, there are different ways to support versioning :-

  • Embed it right within the Resource URI  – e.g. “/v1/users” – this will allow you to continue supporting v1 of your API even when you are working on something new. e.g. you can have a “/v2/users” in 6 months time and you can tell your users to migrate their codebase to use v2 and gradually shut down v1 when you are sure that no one is using v1 anymore. Much better than sending everyone in panick mode by updating “/users” straightaway. I would keep it simple as “v1” or “v2” – I don’t like things like “v1.2.1” or “v1.3” – it’s just easier to call it “v1” or “v2”. Easy naming convention.
  • URL Parameter – e.g. “/users?v=1” – I am mentioning it here but infact I am totally against this approach. Let’s say you only have v1 for now and the parameter is optional as there is only one version. So the developers don’t use it asuming it’s the default. 6 months down the line, you add v2 and now developers must pass in either v1 or v2 and they will have to go back and change their code or else they will get unexpected results. I think this is bad design and would not recommend it.

Supported Formats

You must decide what response formats you are going to support. The common ones are XML, JSON, JSONP. A lot of the API’s do just XML and JSON which, to be honest, is more than sufficient for most scenarios. The question is, how to implement the response formats.

  • URL Parameter – e.g. “/users/:username?format=xml”
  • Request Header– e.g. by passing in a request header[xml]content-type:application/xml[/xml]
  • As a file extension – e.g. “/users/:username.xml” or “/users/:username.json” – This is my personal favorite as I think writing URLs this way is something we are all used to anyways and it is a lot more readable and self-explanatory to the human eye.
  • Any or all of the above – Some API’s support all of these, some support only one of the above. It really is down to you on how many options you want to give your users. However, I kind of prefer to support only one way rather than multiple.

HTTP Status Codes

Your API should be able to support the common status codes. I have seen some API’s where the API developers have come up with their own error/status codes and the actual HTTP status code is always “200 OK” – which is pretty bad. Imagine you making a call to an API as follows :-

[xml]/users/:username.json[/xml]

which gives you back a “200 OK” but actually the response is

[xml]{message:User not found}[/xml]

This really should have been a “404 Not found” and that’s what the API should return. As part of a well designed API, your API should return the proper status code for every call. Here are the status codes that can be supported.

Code Description
200 Success!
201 Created. A new resource has been created successfully. The response body is either empty or contains a representation revealing the URI of the newly created resource.
202 Accepted. The request was valid and accepted but not yet processed. The response body should containe a URI to poll for status updates or a token assigned to this request. This allows for asynchronous REST requests.
204 No content. The request was successfull but the server has no response for it.
301 Moved permanently
302 Moved temporarily. The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests
400 Bad request. Look at the accompanying error messages to see why the request was invalid.
401 Unauthorized. Client not authorized to make the request.
403 Forbidden. The request is understood, but it has been refused. Look at the accompanying error messages to see why the request was invalid.
404 Not Found
405 Method Not Allowed. The requested method is not allowed for that resource
406 Not Acceptable. Cannot generate representation with given formats, headers or parameters.
410 Gone, the resource is no longer available.
500 Internal service error.
503 Service unavailable. Maybe too many requests.

Making it easy for the Developers

This is another very important thing. You can write a top notch API on a top notch server hardware but it’s of no use if there is not enough documentation to support it or the developers find it extremely hard to consume the API. I think there are a few things that can be done to cover this :-

  • Documentation – Ofcourse! Document everything. Depending on what tools you use to develop your API – you may be able to generate some documentation from your method/resource definitions in your code. I use ColdFusion and decided to use ColdBox Relax which is a great tool for generating and testing your API. But again, this is based on your tool of choice. Documenting an API is extremely important along with some sample codes to make it easy for developers to easily grasp it.
  • Testing Console / API Playground – This is another very important thing you must do to enable developers to test your API easily. If your API supports complex authentication procedures like oAuth or Digest, then most likely, testing your API is not going to be easy and a developer generally has to go through quite a lot of testing before anything goes into production. Based on your bandwidth and resources, you may decide to develop such a tool yourself but there are some API management tools available that can do most of this for you already. A very good example is ApiGee – they provide a testing console for many of the famous API’s like FaceBook, Twitter, Github and many more. It’s very easy to create your own console for your own API as well, which your developers can then start using to test your API. This proves extremely useful when your API has multiple complex authentication processes in place.
That’s it for this post. Hope you found it useful.
Tagged with:  

3 Responses to Designing a RESTful API

  1. Sander says:

    Nice post. Thanks.

  2. Grant Copley says:

    Good post! I like the idea of separating your API versions in the URL. From your example, I’m assuming you’d want to return a 405 Method Not Allowed error is someone tried to POST to /users/:username?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2011 Anuj Gakhar
%d bloggers like this: