API 101: Designing kick-ass REST APIs

Johanne Andersen
Sep 1, 2019 · 6 min read

Before we dive into how to design a REST API, let’s just briefly get on the same page on what a REST API is.

REST?

A REST API is an architectural style proposed by a guy named Roy Fielding in 2000. Since REST is an architecture, there is no set standard for how to design a “REST API”. Fielding specified a set of constraints that a REST API should follow and these are the only ones we should adhere to.

I’ve already mentioned one of the main constraints for a REST API in my previous post, a uniform interface. This means a client and a server should be able to evolve independently since the interface won’t change.

Another important constraint in REST is that the interface must be stateless, so each request must contain all the information necessary to understand the request.

Caching is also a big deal in REST, as Fielding put it in his paper: “The most effective architecture styles for a network-based application are that can effectively minimize the use of the network”

Then there are also constraints on the interface interactions:

The goal of REST is to hide behaviour behind entities, so the server is free to implement whatever behaviour it wants without worrying about the client.

There are many more things to cover for REST APIs, but there are amazing resources out there for the more theoretical stuff. I’ll link to some at the bottom of this post and then we’ll move on to how to design a REST API with the above constraints in mind.

Since REST is more a set of principles than an actual recipe to build APIs, the term RESTful APIs have been coined to signify API that uses REST principles but also uses more technologies on top of it.

Designing an API

The API’s job is to make the application developer as successful as possible — API Design: The Missing Link, Apigee.

The most important thing to think about when building an API is the user. This user will most likely be a fellow developer, so you should build your API with that in mind.

Let’s say we have Alice, a front end developer who wants to build a nice UI to display your data. What would make Alices life easier when she is building her application? What format would she want her data in, how would she want to send requests and what would be easiest for her if you update to a new version?

These are all things you should consider before designing your API. So using some obscure data format to deliver data is perhaps not the best idea even if you would have fun implementing it.

In the previous article, I introduced the to-do list example. Now it is time to expand upon it. Say we want to create an API for some of the numerous to-do list applications being created during tutorials.

Here are the requirements for the API:

  • Users can have todo lists
  • A user can do CRUD operations on their todo lists and todos
  • Users can share todo lists with one another

Since our API users will most likely be new developers, this API should be simple and intuitive to use. Which is why a REST API is a great place to start. No state to keep track of and uniform interface.

So our goal is to build a simple consistent API that is easy to understand and use.

REST Conventions

REST is designed on top of HTTP, so if we stick to the HTTP protocol, we’re already well into building a REST API. So for requests, we should use GET, POST, PUT/PATCH and DELETE messages instead of /get-todo-item, /create-todo-item, /update-todo-item and /remove-todo-item.

Endpoints

REST hides behaviour behind resources, so we don’t want to create endpoints that specify behaviour. Instead, we send different HTTP requests to the same endpoint:

GET /todo-lists
POST /todo-lists
PUT /todo-lists
DELETE /todo-lists

There is no rule about naming, but the recommended standard is to use plural versions of resources. Whether you choose plural or singular doesn’t really matter, just keep it consistent.

Concrete naming is better than abstract naming. todo-lists trumps todo-resource any day.

Response codes

We should also specify what response code these requests can get. Again the default HTTP response codes will get you far.

GET /todo-lists - 200 OK, 404 Not found
POST /todo-lists - 201 Created, 400 Bad Request
PUT /todo-lists - 200 OK, 400 Bad Request
DELETE /todo-lists - 200 OK, 400 Bad Request

Then you can always include more descriptive error messages, but again there are no rules. You should specify the outcome of the request though. Otherwise, the user has no idea that the operation succeeded.

For example

Bad
POST /todo-lists 200 OK
{
"status": "error"
}
Good
POST /todo-lists 400 Bad Request
{
"error": {
"status": "400 Bad request",
"message": "Request must specify an owner"
}
}

Relationships

For resources that are linked to one another, there are multiple ways of querying that. One is not necessarily better than the other

GET /users/<id>/todo-lists
GET /todo-lists?user=<userid>

Just remember to stay consistent, if you offer one type, you should offer it for all your resources. The idea is that a developer should be able to implement your API with a minimum amount of documentation.

Identities

This one is hard to get right. There is a lot of discussions because on one hand an id like friday-todo reads nicer and is easier to remember than 1234567 , but 1234567 is more permanent. There is a really interesting post about it here. Apigee suggests offering both, a permalink that looks like 1234567 and a name that looks like friday-todo so you always have the same identifier, but also a name that is easy to remember when searching.

One thing that is considered good practice is to include a link to associated resources, so the user won’t have to extract it.

GET /todo-item/123
{
"self": "/todo-items/123"
"id": "123"
"name": "Pick up keys"
"parentLink": "/todo-list/1"
...
}

Response bodies

How your response body should look will be very dependent on the data you offer. One general rule to remember is; respect the JSON properties. They should not describe individual resources but should be generic to the resource.

So, in our todo list example.

GET /todo-lists/<id>
BAD
{
"id": integer,
"todoItem1": TodoItem,
"todoItem2": TodoItem
}
GOOD
{
"id": integer,
"todoItems": [
TodoItem,
TodoItem,
...
]
}
GET /todo-lists/
BAD
{
"<todo-list-id1>": {...},
"<todo-list-id2>": {...}
...
}
GOOD
{
"todoLists": [
{todoList},
{todoList},
...
]
}

Partial responses

If you want to be really cool, you should consider offering a query parameter for partial responses. This is a response where the user can specify which fields the user wants and the response will only contain those fields. Again, be consistent, if you offer it for one, you should offer it for all.

GET /todo-lists/<id>?fields="name, owner, createdDate"

Recap

A REST API only requires the following specification:

  • A small set of well-known URLs
  • The data model of each resource
  • Which HTTP features are implemented
  • Optionally some query syntax

If you need more than this you’re probably venturing beyond REST. It is not necessarily a bad thing but threads carefully.

I’ll end on a thought-provoking quote from Apigee. Something worth keeping in mind when designing an API.

“A significant part of the value of basing your API design on HTTP and REST comes from the uniformity that it brings to your API. In essence, when you use HTTP natively, you don’t need to invent an API at all — HTTP provides the API, and you just define the data in your resources.”


Thank you for reading, I hope you found it helpful! Let me know what you think, I am still learning myself and would love to know about your experiences. The full specification for the todo API can be found here and in the next post, we’ll start building it.

← Previous post: What is an API?

Next post: The basics of building a RESTful API


Data Driven Investor

from confusion to clarity not insanity

Johanne Andersen

Written by

Software Developer, learning enthusiast (obsessor…) and lover of sleep. Basically, a recent CS grad trying to adult.

Data Driven Investor

from confusion to clarity not insanity

More From Medium

More from Data Driven Investor

More from Data Driven Investor

More from Data Driven Investor

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade