Friday, August 29, 2014

How to create a Web API no one wants to use. Part 3: HTTP Status Codes

Introduction to this blog series


Later this year, Netflix will be closing their public APIs. Twitter and Google have already restricted their APIs. Despite prominent tech-companies making drastic changes to their APIs, the number of APIs keeps growing steadily. Suddenly, everyone has a Web API. Your car has one, Chuck Norris has one, and worst of all, tech-companies with no focus on quality whatsoever has one. And who are using these APIs? Most likely, no one.
This blog series will take you through the pitfalls of creating a Web API. From a developers perspective, what mistakes will result in no one wanting to use your API?

Part 1: URI Design
Part 2: HTTP Verbs
Part 3: HTTP Status Codes
Part 4: Result formatting
Part 5: Versioning
Exercises

HTTP Status Codes

When working with a public Web API, your greatest friend is Fiddler or Postman. Using those, you can debug your web requests and the responses given to you in return: 
The first thing any developer does when they issue a web request against an API is to check the HTTP Status Code of the request. Is the result a 200 OK? Yes, awesome! 
If you find pleasure in giving developers false hope, you should make your Web API return a 200 OK, even though the request was not successful. These types of "errors in disguise" will make developers flee from your API in no time. Saying "Your request was successful! ... Just kidding, gotcha!" is not a great way to attract users.  
Example: The World Bank API

In part 1 of this blog series, we had a look at the URI design of the The World Bank API. Let's go back to that API to see how that are handling their HTTP Status Codes. 
First, ask for all information they have on the country of Brazil: 
GET http://api.worldbank.org/countries/br?format=json 200 OK
As this is a valid request, all is good so far. Now, what happens if we specify a countrycode that does not exists, such as 'a'?
GET http://api.worldbank.org/countries/a?format=json 200 OK
That also returns a 200 OK, so maybe we were mistaken. Maybe 'a' is a valid country code afterall? Let's take a look at the response just to be sure: 
[{"message":[{"id":"120","key":"Parameter 'country' has an invalid value","value":"The provided parameter value is not valid"}]}]
Now, that's just mean. Let's try another one. What happens if we specify the format with a colon instead of an equal sign?
GET http://api.worldbank.org/topic?format:json 500 Internal Server Error
That's better, at least we're not given a 200 OK. We're given a 500 Internal Server Error, and they are even giving us a detailed outline of what is wrong: 
{"fault":{"faultstring":"Evaluation of script AssignQueryParamsCaseInsensitively.py (py) failed with reason: \"TypeError: cannot concatenate 'unicode' and 'NoneType' objects\"","detail":{"errorcode":"steps.script.ScriptEvaluationFailed"}}}
Problem: The World Bank API

The first issue here is that the API is returning 200 OK statuses despite there being something wrong with the request. The HTTP/1.1 protocol clearly states that 200 OK should be used when "The request has succeeded". 
The main issue, however, arises when we supply the incorrect format. The request produces a 500 Internal Server Error, although what really caused the issue was us supplying an invalid request. To make matters worst, the result contains a detailed error message. Displaying the internals of your Web API to the public makes it vulnerable to attack, and you should never ever return a stacktrace of the errors produced. 
Solution: The World Bank API

When you're developing a Web API, you should at least use the three most common HTTP Status Codes: 
Status CodeDescriptionFor dummies
200 OKThe request has succeededAwesome!
400 Bad RequestThe request could not be understood by the server due to malformed syntaxThe user of the API made a mistake
500 Internal Server ErrorThe server encountered an unexpected condition which prevented it from fulfilling the request.The creator of the API made a mistake
In addition to these, if you're serious about your API, you should also take a look at 201 Created, 304 Not Modified, 401 Unauthorized and 404 Not Found.
In the examples above, the request where we specified an invalid country code should have given us a 404 Not Found:
GET http://api.worldbank.org/countries/a?format=json 404 Not found

Specifying an invalid format should not have produced a 500 Internal Server Error, it should have given us a 400 Bad Request (without displaying its stacktrace):
GET http://api.worldbank.org/topic?format:json 400 Bad Request

Summary


HTTP Status Codes exist so that users of your API immediately know whether or not the request they made was successful. In the cases where the request was not successful, the status codes gives them a clear overview of why it failed. Disguising your errors as 200 OK's will only help in building up irritation, so you should consider using at the most common status codes when building your API. Also, do yourself a favour, and care about your API from the very first piece of code you write. 

No comments:

Post a Comment