In my first blog REST Easy we discussed how the simplicity and scalability of REST web services are causing it to become increasingly popular. In this post we are going to dive into some of details and best practices for designing a REST API.

Resources

Resources are the foundation on which all REST APIs are built. A resource is a representation of the state of a thing or idea, a noun. You can make your API user friendly by designing your resources in a clear, consistent, and predictable way.

Resources Should Always Be Plural

When referring to a collection of resources it makes sense to make the resource plural. For example you would expect the resource ‘/dogs’ to refer to a list of dogs. But what about when you refer to a single dog? Your first thought may be to design it as ‘/dog/rusty.’ The problem with this approach is that it is neither consistent, predictable, nor hackable. If you designed the single dog resource with ‘dog’ instead of ‘dogs’ you would end up with two resources that ultimately refer to the same thing - a dog. Keeping in mind that one of the main goals of rest is simplicity, it makes sense that we should only ever have one resource that represents a dog. By exclusively using plural nouns make our API more user friendly. Without even referring to the documentation, they’ll be able to guess that if we have a collection of resources - ‘dogs’ - that working with a single resource will always be the same resource with a specified id - ‘dogs/{id}’. This keeps the resources hackable, meaning that its easy for developers to find and discover resources on their own. By knowing that the resource ‘/dogs/rusty’ exists they can hack the resource URI by removing the {id} and expect the ‘/dogs’ resource URI to represent the full collection of dog resources.

But if all my resources are plural, how can I support creating a single resource? This is a matter of opinion, but the way I see it you have two options. Option 1 - you don’t. If someone tries to POST a single object for creation you can simply reject the request and require the user to POST the new object wrapped in a collection. Option 2 - silently handle POSTs for single objects under the covers. This means that if the server gets a POST request for a single object instead of an array of objects, you convert it to an array and proceed as if an array was posted in the first place. Whatever route you decide on just make sure you are consistent across all of your resources.

Two URIs Per Resource

Now that we’ve changed all of our resources to be plural let’s see how this helps our design. We can now reduce all our resources down to two basic URIs. One for a collection and one for an item in the collection.

Collection:

/dogs


Item:

/dogs/{id}

Really. That’s it. If you need additional complexity you can tack it on as a query parameter of the resource, such as ‘/dogs?color=black’ - more on this later.

Resources Should Be Nouns, Not Verbs

If you read the first part of this series (link here) you’ll remember that all actions performed on a resource should come in the form of the HTTP verbs - GET, POST, PUT, and DELETE. Without the use of these verbs, you may be tempted to create resources listed below and access them all with a single HTTP GET command.

/createDog	/getDogs	/getBreed
/boardDog /adoptDog /feedDog

Luckily its pretty easy to see that the lack of naming consistency for these resources would get out of hand pretty quickly. Because we didn’t follow any standards or naming conventions while designing these resources, the API user is going to have a more difficult time discovering and remembering all the different variations. A design like this would prevent a developer from using your API without heavily relying on its documentation.

Relations Between Resources

Frequently you may have the need to nest resources that are related to each other. Suppose you want to find all the dogs a certain vet has as patients. You can do this by nesting the ‘dogs’ resource under the resource identifying the particular vet - ‘/vets/{id}/dogs.’ The resulting resource would be expected to contain a list of dogs that are patients of the specified vet.

If you find yourself creating resources nested more than a couple levels deep, chances are that there’s a better way to do it. For example a vet may have a relation to a dog, who has a relation to an owner. You may be tempted to design a resource such as:

‘/vets/{id}/dogs/owners’

In this example we are trying to get a list of all the owners who have a dog at a specific vet. Since the end resource is really just a collection of owners, it would be better to ask for a list of owners that go to a certain vet and filter the list by those who own dogs. We can apply the two URI per resource rule and simply ask for the list of owners and represent the rest of the complexity with query parameters.

‘/vets/{id}/owners?haveDog=true’

or even better

‘/owners?vet={id}&haveDog=true’

An owner may also have a relation to a vet, who has a relation to a dog, who has a relation to a veterinarian, and so on and so on - possibly modeled as:

‘/owners/{ownerId}/vets/{vetId}/dogs/{dogId}/veterinarians’

However, in this case all we really want is the list of veterinarians who take care of a certain dog. Once we know the id of the dog there isn’t a need to nest any further upstream in the resource. Instead we could just ask for the veterinarians who see a specific dog.

‘/dogs/{id}/veterinarians’

Designing resources to be nested one level deep is a generally accepted practice, but I encourage you to carefully think through your design before nesting any deeper than that as there is likely to be a cleaner way.

Partial Resources

We now know how to get a collection of dogs, a collection of owners, and how to filter those collections with query parameters when needed. But what happens if we only need to know the names of all the dogs at the vet? We could make a GET call to a resource ‘vets/{id}/dogs’ then loop through the returned collection to pull out the names of each dog. Although this approach would work, it results in the server sending a lot of unnecessary data about the dogs which will slow down the response. If a user only needs to know about certain properties of a resource you can support a ‘fields’ query parameter where the required data can be specified.

‘/dogs?fields=name’ or ‘/dogs/{id}?fields=name,age,breed’

Now your API users have the flexibility to ask for only the specific data they need.

Responses

There are a lot of different approaches to designing the response format for your API, but your number one priority regardless of your design should be to ensure your format of choice is consistent across all resources. No one wants to use an API where they always guessing what the response for a particular resource will look like.

Data Formats

Traditionally REST APIs support JSON and/or XML data formats. If your API supports responses in multiple formats the user should specify which format they prefer in the Accept headers of the request - ‘Accept: application/json’ or ‘Accept: application/xml’. If the format isn’t specified by the user the API should always default to the same format (which is generally JSON).

Status Codes

When a response is sent back to the user, we need to indicate if the request was successful or if something unexpected happened. Luckily HTTP already defines a list of status codes for us to use. You don’t need to use every available status code, but you should document a list of all the possible status codes your API will return. You are free to choose which status codes to support, but for simplicity I recommend keeping the list size around ten.

200 OK - Request was successful.
201 Created - Request was successful and the resource was created. Used with PUT/POST.
204 No Content - Request was successful, but no content will be returned. Used with DELETE.
304 Not Modified - If caching is being used, it indicates that the response has not changed since you last requested it.
400 Bad Request - Request was malformed. Generally means that bad data was provided.
401 Unauthorized - The resource requires authentication and you didn’t provide valid credentials.
403 Forbidden - The request has a valid login, but doesn’t have permission to access the requested resources.
404 Not Found - The resource doesn’t exist.
429 Too Many Requests - If the API has a rate limit and you made too many requests in the allotted time period.
500 Internal Server Error - An unexpected error happened on the server.

Error Handling

Defining a standard error format is critical for the usability of your API. Users will expect to see a consistent error format across all resources. For example an errored response for a GET /dogs request may look like this:

403 Forbidden
{
 "error": {
   "status": 403, // HTTP status code
   "code": 12, // internal error code
   "message": "User does not have read access for dogs."
 }
}

Inside the error we included status, code, and message attributes. The status and message attributes are self explanatory, but what’s interesting is that we’ve included a value for an internal error code. This is optional but the additional detail can be provided if its useful for your business requirements. Some APIs even include an attribute with a link to additional documentation about their custom error codes.

While it’s important to keep the same error format across your API, it may be necessary to structure your response body differently based on the HTTP action being performed. When doing a read (GET) request it makes sense that the request would return everything requested or nothing at all. This means the GET error response body can always be structured like the above example no matter how many dogs were requested.

Create requests can get a bit more complicated depending on the functionality of your API. Some APIs are designed so that all POST requests are transactional - meaning that if any of the items included in the post fail, nothing will be created. In this case we can stick with the simple error format above because if an error occurs the user will know they have to retry everything in the request if needed. But what happens if we design our API to create all possible resources even if some of them fail - meaning one failure doesn't rollback everything? The response body will need to tell us which resources were successfully created as well as which one failed and why. It no longer makes sense to mark the entire response as errored just because one resource wasn’t created. In this case we’d want to set the status as successful and let the API user parse the result to check for any errors. Here’s an example.

Request:

POST /dogs
[
 {
   "name": "Rusty",
   "breed": "mix"
 },
 {
   "breed": "pitbull"
 }
]

Response

200 Ok
{
 "success": [
   {
     "_id": 1,
     "name": "Rusty",
     "breed": "mix"
   }
 ],
 "failure": [
   {
     "error": {
       "status": 400,
       "code": 10,
       "message": "Cannot create a dog without a name.",
       "content": { // original data that caused the error
         "breed": "pitbull"
       }
     }
   }
 ]
}

We attempted to POST two new dog resources - one was successfully created and the other errored because it was missing a required field. You’ll also notice that the error contains the resource content that failed so that the API user can easily locate the bad data in case they want to fix it and try again. You can use this same strategy for non-transactional bulk update (PUT) and delete (DELETE) requests as well.

To summarize, if your API only supports transactional - all or nothing - requests then you can stick to the simplified error format with a single error code and a description of what failed. However if you support non-transactional operations - allowing for successful and errored resources in a single request - then you should split your response into success/failure blocks so the API user can process the results accordingly.

Discoverability

There is a lot of debate on the topic of discoverability of REST APIs - not in the sense of what it is, but whether or not it’s necessary to implement. It all begins with the concept of HATEOAS (Hypermedia as the Engine of Application State) which is one of the constraints a truly RESTful API. Implementing HATEOAS means that your API provides information about a resource and how to use it by providing additional links in every response. Since every resource contains links to its own functionality it is largely self documenting. If you were to ask for a list of dogs where the only actions you could do to a dog resource was GET and DELETE the response would look something like this:

GET /dogs
[
 {
   "_id": 1,
   "name": "Rusty",
   "breed": "mix",
   "links": [
     {
       "rel": "self",
       "href": "https://api.makeandbuild.com/dogs/1",
       "method": "GET"
     },
     {
       "rel": "delete",
       "href": "https://api.makeandbuild.com/dogs/1",
       "method": "DELETE"
     }
   ]
 },
 {
   "_id": 2,
   "name": "Callie",
   "breed": "pitbull",
   "links": [
     {
       "rel": "self",
       "href": "https://api.makeandbuild.com/dogs/2",
       "method": "GET"
     },
     {
       "rel": "delete",
       "href": "https://api.makeandbuild.com/dogs/2",
       "method": "DELETE"
     }
   ]
 }
]

Not only did the response contain the data representation of all the dogs, but it also told us how to get and delete the individual dogs in the list. By providing the links directly in the response we’ve made the API easier to use, but with everything there are tradeoffs. The most obvious impact of adding links is that even in the small GET /dogs example we’ve more than doubled the size of the response. It will also take a non trivial amount of time to develop and maintain up to date links for every resource. So the question remains, should you implement HATEOAS for your API? The answer is it depends. If you’re working on a small project that is likely only going to be used internally by your own team it may be overkill to implement links for every resource and its actions. Sure you won’t be able to say your API is truly RESTful (because it doesn’t follow all REST principles) but in the real world this is a perfectly valid tradeoff. On the other hand if your API is expected to be used by a large number of third parties and you have the time and resources available to implement it your API users will surely be appreciative.

Since REST only provides guidelines to follow when designing an API and each API has unique business requirements to meet there will always be differing approaches of how to best design your API. My hope is that this article has given you a good foundation and has provided valuable insight on some of the major design decisions you’ll still need to make for yourself along the way.