Monday, September 1, 2014

How to create a Web API no one wants to use. Part 4: Result Formatting

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

Result Formatting

The developers consuming your API will have varied opinions as to what format they want their results in. Supporting only XML might make developers who prefer JSON, JSONP or Turtle turn to other APIs instead of yours. In order to please the users of your API and to make sure you're not dictating the formats, you should support at least a few of the most common ones.

Example: Norwegian Meteorological Institute's Weather API

The Norwegian Meteorological Institute's Weather API provides forcast data for it's users. What we are interested in knowing is how they format their results. Let's ask for the sunrise data for Oslo, Norway:
GET http://api.met.no/weatherapi/sunrise/1.0/?lat=59.922086;lon=10.752844;date=2014-08-16

And we receive an XML result. According to ProgrammableWeb, XML is on decline and more and more API users prefer JSON (http://www.programmableweb.com/news/1-5-apis-say-bye-xml/2011/05/25). So APIs only supporting XML is decreasing in popularity:


Obviously, formatting your results only in XML is not a good idea then, you should also support other formats. So how are Web APIs out there doing that? Let's take a look at some of the different solutions:

Solution 1: URL extension

Some APIs let the user specify the format they want as a URL extension. I'm not a huge fan of this solution, but it's better than not supporting multiple formats so let's have a look at it.

In part 1 of this blog series, we had a look at the URI design of the U.S. City and County Web Data API. Let's ask for all links they have for Chicago, Illinois:
GET http://api.sba.gov/geodata/all_links_for_city_of/chicago/il

This request gives us XML in return. Now, let's ask for the same thing, specifying that we want the result in JSON:
GET http://api.sba.gov/geodata/all_links_for_city_of/chicago/il.json

We use the same URL, but we add 'json' as an extension. We can also add 'xml' as an extension and get the same result as we got when we didn't specify the format. In other words, if you don't supply a format extension, the fallback is XML.

The problem with using URL extensions for result formatting is that your API will have two different URLs that supply the same data. Having two separate URLs suggest that these are two different resources. However, they are not different resources, they are simply two different representations of one resource. So let's try another approach!

Solution 2: Query string parameter

A lot of APIs accept the format as a query string parameter, which of course solves our previous issue of having several URLs for the same resource.

In part 1 of this blog series, we analyzed the URI design of the World Bank API and as we saw then, they use a query string parameter to specify result formatting. Let's get all the countries:
GET http://api.worldbank.org/countries/all

As in our previous example, not specifying a format defaults to XML. So our request above is identical to:
GET http://api.worldbank.org/countries/all?format=xml

Now, if you want the result in JSON instead, you set format=json:
GET http://api.worldbank.org/countries/all?format=json

As you can see, each resource only has one URL and the different representations can be retrieved by including a query string parameter. But what if we don't want to specify the format, what if we instead want to say "These are the formats I support, give me one of them"? This can not be done with a query string parameter, and that brings us over to Content Negotiation.

Solution 3: Content Negotiation

Best practice when it comes to result formatting is considered to be Content Negotiation. Using Content Negotiation, the client tells the API which formats he is able to accept, and the API selects the first suitable format. Let's take a look at how the Facebook Graph API uses Content Negotiation. By default the API returns its results as text/javascript:
GET http://graph.facebook.com/daft-punk

However, if you want the result as JSON, you need to add an Accept header to your request:
HEADER Accept: application/json
GET http://graph.facebook.com/daft-punk

The Content-Type of the response will now be JSON instead as you've specified that you prefer JSON. You can add several formats to the Accept header. Let's see what happens when we add both JSON (which the API supports) and XML (which it doesn't support):


The Content-Type of the response is still JSON as the API picked the only format it knew it supports. Let's add another format to the Accept header, the text/turtle format.


In this request, we've added three formats to our request Accept header: application/json, text/xml, and text/turtle. The API only supports the first and the last, and therefore it picks the last one in the list.

By supporting Content Negotiation, you allow the users of your API to give you several options as to what result format to return. And that's how you make your API a walk in the park to work with.

Summary


When you're developing an API, it's important to remember that the users will all want different things and they all prefer different formats. There are several ways of letting your users specify the format they want, for example by using URL extensions, query string parameters or Content Negotiation via accept headers. There's a lot of discussion as to which of these is the "most correct", but what's most important is that you give your users a choise and care about your API from the very first piece of code you write.


No comments:

Post a Comment