These days there’s a lot of money in mobile applications – and where there’s money and new technologies there’s web developers primed and ready to argue about how best to implement these new technologies.
As one of this dedicated crowd I’ve found myself recently working with a lot of Rails RESTful APIs that talk to my mobile applications. This has given me the opportunity to both create my own RESTful APIs and use RESTful APIs written by other developers.
As a result, I’ve gained a much better understanding of the HTTP Status Codes and how they can help you write both better APIs and client applications.
For clarity, I use the term ‘client’ to refer to the mobile application that is communicating with the Rails RESTful API backend.
When I say API I’m referring to a Rails RESTful API.
Before I begin, here are some good sites for reading up on the HTTP Status Codes:
- ietf.org (Internet Engineering Task Force) RFC2616
- w3.org RFC2616
- httpstatus.es
- Wikipedia List of HTTP Status Codes
- REST Patterns HTTP Status Codes
Here’s a good article by Alex Rogriguez over at IBM from 2008 on REST APIs. If you aren’t clear on what a RESTful API actually is give Alex’s article a read.
Here’s an example on how Twitter handles HTTP Status Codes and a RESTful API
For more examples you can do a quick Google Search and you’ll find the REST API docs from Microsoft and Dropbox, among others.
Finally, for the super-keen, here’s a list of the stackoverflow questions on ‘rest’ and ‘api’ sorted by votes.
Now, back on point!
Why are Status Codes important?
The status code works hand-in-hand with the response body to help the client application process the request.
Status codes tell the client application what it needs to do with the response in general terms.
For example, if your API returns a 401 you know you need a logged in user. If the API returns a 403 you know the user you’re working with doesn’t have sufficient authorization. If the API returns a 418 you know that the API is telling you it’s a teapot and was probably written by knobs.
The response body will tell your client application in specifics what it needs to do with the response. If the client is trying to update a record and the API responds with a 400, the response body will inform the client why the request failed. On the other hand, if the API responds with a 200 the response body will provide the client with the updated entity.
That seems pretty straight forward, right? The difficulty lies in when the API returns a status code and a response body that don’t match up.
I worked with an API that returned a 200 status code but the response body had an errors array in it. That was an absolute trip – I had to parse the response body and look for the presence of content in an errors array first. Then I called an error handler from within the success callback of a jQuery ajax function. This is an example of a bad RESTful API. I hope by reading the remainder of this article you can avoid doing that.
Which status codes should I be using?
As long as you’re not too pedantic status codes are easy to work with when writing an API.
Here’s a rough guide to how I handle mine:
(For the purpose of all of the examples below, any response content I refer to will be as JSON.)
(2xx) Success Codes
200 OK
The 200 OK status code can be your go-to for any successful response. There are many success codes but for a very basic API I’ve found the 200 *can* be expressive enough. The distinction between 200 and the other success codes hasn’t proved valuable in my APIs so far.
See the IETF docs on the 200 status code.
However, some other Success Codes you might be interested in:
- 201 Created for when you’re creating a new resource.
- 202 Accepted for when you’ve successfully set the request to be performed in a background task. Useful if your client is requesting something on the API that is time-consuming and you don’t want the client to have to wait.
What should I return in the 200 response content?
The content of the response is dependent on the HTTP Method used in the request.
Quoting from the IETF RFC2616 docs:
GET an entity corresponding to the requested resource is sent in the response;
POST an entity describing or containing the result of the action;
So consider an example where you are performing a request something like:
GET api.example.com/1/users?pirate_or_developer=true
Your response JSON will look like:
[{id: 1, name: 'Jordan'}, {id:2, name: 'Guybrush'}]
This JSON should allow the client to update all the records they have that match the records provided in the response. Possibly, it will also allow the client to determine which records have been deleted and can prune its local storage accordingly.
Now, consider an example where you are updating a resource with a request like:
PUT api.example.com/1/users/1
Your response JSON will look like:
{id: 1, name: 'Jordan'}
This allows the client to update its data for the record.
(4xx) Client Error Codes
Here I’ll cover the 3 client error codes I find myself using most in my APIs:
400 Bad Request
The 400 Bad Request status indicates that the request ‘could not be understood by the server due to malformed syntax’. See the IETF docs
There is also the 422 Unprocessable Entity status code that appears to be popular for roughly the same purpose. I’ve read from a couple of sources that since the 422 is part of the WebDAV extension that 400 is preferable over 422. I don’t have the knowledge to weigh in on this argument. Perhaps someone can enlighten us in the comments section.
Some examples scenarios I’ve used an API to return a 400 for:
- The client is trying to create a resource with data that fails validation rules on the API
- The client is requesting resources using invalid params (e.g.: the date is malformed)
- The client request is missing fields the API requires as indicated in the thoroughly written docs! (e.g.: a search might require an end date is a start date is passed through)
What should I return in the 400 response content?
The response of a 400 will depend on why the request failed.
Likely, you’ll want to keep a consistent format across all API end-points. I nest my failure reasons in an errors attribute in the JSON.
As an example, let’s say the client request failed because the name provided was too long. The API will respond with:
{errors: {name: "must be fewer than 18 characters"}}
In fact, given this is being returned with a 400 status code, you can probably do away with the errors wrapper entirely and only return:
{name: "must be fewer than 18 characters"}
The only important thing is to be consistent across your API in how you represent these errors.
401 Unauthorized
The 401 Unauthorized status indicates that the request had missing/invalid authentication credentials. See the IETF docs.
To demonstrate the use of the 401 in an API, I’ll use one of my apps as an example.
In the client application we have a login screen for entering email/password to log in. On all other pages in the application we use token-based authentication (each request must have an token passed through that is checked by the API before performing any request). When the client successfully logs in the API passes a token back that can be persisted on the client device.
In this scenario, the API will return a 401 if:
- The client app attempts to authenticate a user by passing invalid username or password
- The client app makes a request to an action that requires authentication without providing the token (To be honest, I think this has only ever happened to me in development)
- The client app makes a request to an action that requires authentication providing an invalid token (For example: the user tries to log in to the client app after their user has been disabled on the API)
What should I return in the 401 response content?
I’ve found that so far I’ll just return one of:
{errors: {invalid_credentials: 'Authentication credentials provided were invalid'}}
or
{errors: {no_token: 'No authentication token was provided in request'}}
or
{errors: {invalid_token: 'Token provided was invalid'}}
403 Forbidden
The 403 Forbidden status indicates that although the request was valid the action requested failed authorization constraints. See the IETF docs.
Pretty straight forward scenario: user x tries to update resource y. User x is not authorized to update resource y.
As a note, if you are making a client where you strictly control the interactions with the API I feel that your client ever getting an authorization error is at best a code smell but more likely it’s actually just a bug.
Consider an interface that has a button that the user can interact with which returns a message like ‘You are not authorized to perform this action’. Ideally, in a mobile interface the user should never see an authorization error message since it’s not something the user can do anything to fix.
What should I return in the 401 response content?
The API will return a string with a simple error message like:
{errors: {authorization: 'You are not authorized to perform this action'}}
What about the rest of the client error status codes?
So I’ve given you the top 3 error status codes I use in a bit more detail but that’s not an exhaustive guide to the error codes. Here’s a whirlwind summary of the remaining error codes that I’ve used in my APIs:
- 404 Not Found for when the URL is wrong or the requested resource does not exist.
- 410 Gone for when the resource has been permanently (and intentionally) deleted. This informs the client application that it should remove any references to the resource in question.
Summary
I hope I’ve provided some insight into the various status codes you can leverage to create an accurate, informative API. I also hope I’ve illustrated the strong relationship between the status code and the response body and the importance of providing a descriptive response body.
If you have any thoughts or disagree with me on this let me know in the comments and we can discuss!
Thanks for reading.
Rebecca Skinner
Aug 21, 2012
Good article, but the part about using a 400 response code for validation errors strikes me as wrong – there’s nothing ‘malformed’ about the syntax of the request, but the server has decided it shouldn’t honour it for business reasons.
Wikipedia cites a 422 response code as ‘The request was well-formed but was unable to be followed due to semantic errors.’ which seems to make more sense for this case.
Jordan Maguire
Aug 21, 2012
Yeah – the 400 was a tough call. I had seen a number of examples of 400 being used for this purpose and the explanations provided were persuasive so I went with it. I wish I had the articles here but I’m sure I can dig them up tonight.
Initially I had used 422 instead of 400 but a particularly vitriolic stackoverflow article on the topic changed my mind. Also, the absence of 422 usage in all of the RESTful APIs I came across while researching this swayed me even further.
Interestingly, it appears that twitter use 406 Not Acceptable to cover invalid formats in requests. This actually sounds quite plausible and if I find some time to do some more reading into it I may update my APIs to use this.
Another interesting note – I read somewhere deep in the depths of stackoverflow that the 403 Forbidden status code should be used. I believe the reference the author made was to the part of the description that says “The server understood the request, but is refusing to fulfill it.”
Jordan Maguire
Aug 21, 2012
Funny story – Github uses 422.
Luca
Aug 21, 2012
Hi Jordan, great article!
which libraries did you use for your RestAPI?
Jordan Maguire
Aug 21, 2012
Hi Luca,
For the most part I rolled my own.
I have also used RocketPants by Darcy Laycock (https://github.com/filtersquad/rocket_pants) which makes developing APIs a breeze.
Luca
Aug 21, 2012
Hi Jordan,
thanks for the suggestion!!
Jordan Maguire
Aug 21, 2012
Luca – no worries. Hope it makes life easier :)
Samuel Cochran
Aug 28, 2012
422 Unprocessible Entity is the Rails convention, per the generated templates (https://github.com/rails/rails/blob/master/railties/lib/rails/generators/rails/app/templates/public/422.html) and the default responders (https://github.com/rails/rails/blob/d168c1f7a779c6b0322a747b7accf035fe7b3db3/actionpack/lib/action_controller/metal/responder.rb#L259).
It’s useful to have a semantic difference between “The request was just bad”—a 400, which might mean weird params, or something just plain wrong in HTTP—vs. “you submitted an understood but incorrect request”—a 422 Unprocessible Entity—which probably has a semantically–relevant set of errors which can be presented to the end user for correction.
Jordan Maguire
Aug 28, 2012
Ohai Sam,
Yeah – moving forward I’m going with 422. I’m having difficulty differentiating between 400 and 422 – pretty much every scenario I come across the 422 seems more correct (ie: well formed request but can’t be processed due to validation errors or whatever on API).
Do you have any specific examples of where a 400 would be used over a 422 in a Rails environment?
Ryan Cheung
Jan 11, 2013
Great post! I do mostly the REST API things as you. It really shows me a better way to consist response content of different status codes. And I think it’s important to keep the error responses consistent, which would make it more convenient to process for mobile clients.
Jordan Maguire
Jan 11, 2013
Hi Ryan,
Glad I could help.
Developers of clients using your APIs will be extremely grateful if you keep everything consistent :)