As we integrate multiple data sources into apps and websites, great API design gets ever more crucial. Lorna Jane Mitchell sets out seven tips for designing better RESTful APIs.
The prospect of creating an API for your app can seem a daunting one. Being aware of a few simple principles makes the task much more straightforward. This article will set out seven tips for creating a successful API, and explore the much-misunderstood topic of RESTful design.
01. Understand HTTP
HTTP (HyperText Transport Protocol) is the basis of all web traffic. But the web is more than the URLs taking us to pages we can view. Great APIs depend on understanding and respecting the web's client/server model; all web journeys involve a series of steps, where each is a request/response pair. The web is 'stateless', which means we need to send with each request all the information the server will need to provide the desired response.
Much of this is invisible to the client or user. We use verbs to dictate what kind of operations should take place, such as GET or POST operations. HTTP itself is a format designed to carry metadata as well as actual body data, sometimes called an 'envelope format': there's information on the outside as well as the contents inside it. What we don't usually see is mostly in the HTTP headers, present in both request and response. These contain information about the client making the request, the data formats being exchanged, what can be cached and for how long, about authentication, and myriad other things. If I open the www.netmagazine.com home page in my browser, it generates the request and response headers in the Wireshark screenshot below.
02. Create hackable URLs
We've all looked at the URL of a web page and edited it to suit our needs. When this works, taking us to a related page that we hoped to access, the URL is 'hackable': definitely a feature of good APIs. Consistent design means users can guess where to find things and navigate your API intuitively. In the GitHub API, accessing a list of the user's repositories:
...helps us to easily guess where to find the user details:
At this point, can you guess where to find the gists for this user?
What you see is a RESTful style of URL. REST stands for REpresentational State Transfer; its name gives clues to how a service like this operates. Working with RESTful services involves literally transferring representations of data! We always work with whole representations: the URLs identify each 'thing' we have.
Every item in the system is a resource: for example, a user, a repository, or a gist. Each exists independently in its own right and has a unique identifier – a URI (unique resource identifier) or, in the case of a web setting, a URL.
Some URLs do not refer to a single resource, but return a list of them; for example, the list of gists. These endpoints are called 'collections', and contain multiple resources: directories are to collections what files are to resources.
When creating a first RESTful service, one starting point can be to consider your underlying data model. This way, your tables become collections and rows become resources. It's a very simplistic model, but it works as a place to begin!
03. Use verbs and URLs, not verbs in URLs
When we make a GET request to GitHub to get the list of the gists a user has, we make a GET request to a particular URL:
When we want to create a gist on that user account (if we have permission to), we use the same URL, but instead of making a GET request, we make a POST request and send a representation of the new gist with it as body data.
Using verbs to dictate which operation is performed is the only time we do so in a RESTful service. A verb in the URL, such as setUserEmail, means something non-RESTful is afoot! Devs work mostly with functions (always verb-named) so this is a big leap of faith in many ways. A common pitfall is to create a nice resource URL such as /article/4, and then introduce /article/4/publish as an endpoint used to send articles live. A more 'correct' way to do this would be:
- GET /article/4
- Amend the 'published' field in the representation
- PUT the representation back to /article/4
But sometimes we don't want to transfer a whole article around just to set the published flag. There are ways to get around this. For example, you could create an additional subresource to represent just that piece of information, as shown in the image at the top of the page.
04. Make status codes the messenger
The status code forms part of the response header and gives the client the 'headline news' as to whether the request was successful or not. This is a much better way of giving information than the status=success fields you sometimes see in API response formats, and as such it's vital to pick a correct, informative code for all responses (including the unexpected cases such as errors).
You are probably already familiar with some status codes: 200 means something went well; 302 means that everything is good, but there was probably a rewrite rule involved. Anything in the 400 range means the user did something wrong - and anything in the 500 range means that the issue lies with the server! Here are some of the codes most commonly used:
Not many applications need to implement a wide range of status codes, but Wikipedia has a comprehensive list. (There is also an entirely frivolous one for anyone who likes cats: www.httpstatuscats.com.)
05. Design for consumers, not developers
We've covered using our data model as a starting point for designing a RESTful service. If designing a database, we start by normalising data for consistency, but often denormalise it for performance or usefulness. Similarly we might pick a data structure as a starting point, but a useful service hinges on assessing how users will interact with it. Their stories can be a great way to begin: think about who your users are and how they will utilise it. There's an element of following the saying, 'pave the cow paths'; you may iteratively alter your system to reflect who turns out to be using it once it is live, and what their usage patterns are.
There are lots of points to consider when creating any API, but one design decision in RESTful services is around how much data to include with a request, and how much to exclude to offer on sub-requests. Consider the relationship between articles and authors (see image, previous page). It would be fitting to offer the ID of the author or a link to the author resource when serving an article resource. Or you could help consumers and add the author's name to the article resource, perhaps as well as a link to the author resource.
06. Offer appropriate data format(s)
We have already discussed the importance of understanding the HTTP headers and using them well. APIs may take one or many different data formats – common ones include JSON and XML, but other formats are equally valid – and it's common to offer more than one format to consumers of your API.
The idea of using multiple formats sounds great, but how do you determine which format a client would prefer? Answer: simply inspect the Accept header of the incoming HTTP request. This might specify a single format preference, or a selection that can be understood. Here's the Accept header from that initial web request to the www.netmagazine.com homepage:
The Accept header consists of a series of comma-separated values, with an optional q rating. By default, q is considered to be one, but lower values denote options that are still acceptable but less desirable. The example header here is requesting HTML or XHTML+XML. If neither is available, XML is acceptable, and as a last resort Chrome claims it understands all other formats by asking for */*.
All requests and responses that contain body data will also have a header describing what format the body is in: the Content-Type header. Using Content- Type with Accept to request one of a short list of formats and then describe which format was actually sent is called 'content negotiation'.
Choosing formats can be a puzzle, but each one has its advantages. Pick JSON when you want a small overall volume of data to transfer, and for simple decoding on less powerful devices such as mobile phones. XML, meanwhile, is a great choice where a more accurate, verbose format is needed and is especially favoured by Java and .NET platforms. But other formats can also be helpful: how about a simple HTML output format for GET requests? Devs can use it to browse the API (and are more likely to click around than to read documentation!).
07. Level up with hypermedia
Did you spot the hyperlinks between resources in the HTML output screenshot above? This is known as 'hypermedia', what a hyperlink would be if it was in an API; it enables resources in a system to be aware of their connections to one another. Taking the example of an article and its author, we might serve an article resource including not only the author's name, but a link to the author resource itself. By adding links to related resources as part of a resource, your API's users can start to find their way around and be aware of the functionality available to them: functionality they might not find using the documentation.
While a service that uses hypermedia might seem to be almost self-documenting, this design approach doesn't replace real API documentation! Whether you write your API documentation by hand or create it using a tool of some kind, it is essential to provide reference materials for consuming users. It is also very important to provide worked examples and tutorials, explaining how to use your API in order for users to be able to get started.
Bonus tip: know when to break the rules
As with all kinds of software design, there's a time and a place to break rules! Every API is different and the occasions when there is only one 'right' way to do anything are rare. The tips here will point you in a good direction and build on trails already blazed in this area, but pragmatism always wins out over theory.
For example, you might choose to support a format parameter on the URL as well as using the Accept header if users generally won't have experience of working with HTTP headers - or you might allow POST rather than PUT requests if you know your API may be consumed by clients with limited capabilities. As with a website, when you build an API, you aim to create something useful for a certain task. So go forth and build something your users can use and love!
Words: Lorna Jane Mitchell
This article originally appeared in .net magazine issue 238.
Liked this? Read these!
- Check out these amazing HTML5 websites
- Brilliant Wordpress tutorial selection
- Our favourite web fonts - and they don't cost a penny
Got a question? Ask away in the comments!