Jonathan Tweed

designing apis: our techniques and principles

As we work towards Atlas Deer and API 4.0, I’ve been spending some time thinking about API design. We thought it would be good to outline our current thoughts and the principles we adhere to when designing REST APIs. Together these form the basis for the design of all our APIs. Let’s start with our principles.

principles

Our REST APIs adhere to the following principles:

  • Use plurals for resource types
  • Resource type URIs represent a collection of all resources of that type
  • Resource type URIs support querying of resources of that type
  • Every resource has a single canonical URI under its resource type
  • Support the common use cases in simple, obvious ways
  • Ensure a guaranteed, consistent speed of response for more complex queries

semantics

Key to making the most of these principles are some standard semantics for HTTP methods. Using a resource type of topics as our example:

URI /topics /topics/:id
GET Returns a paginated list of all topics Returns a single topic
POST Creates a new topic Creates a resource subordinate to the topic
PUT Updates the list of all topics Updates a topic
DELETE Deletes all topics Deletes a topic

taking things up a notch

So far, so good. But that’s all standard, and important, REST API design. How can we make things easier for clients to use and meet some of the harder use cases we will inevitably be asked to support? Our answers: annotations, flexible resultset querying, reuse of common resultsets as subordinate resources and inline documentation.

annotations

Giving the consumer control over how objects are hydrated is important. Annotations allow API users to be more specific about the data they require, reducing the size of API responses. It also allows us to continue adding new things to the API without increasing the amount of data returned for everyone.

We also use annotations as “useful bundles of stuff”. This gives us the flexibility to add annotations for specific groups of fields if required to support a particular use case. Or indeed create an annotations that changes the semantics of the data returned in a particular circumstance. An example of where we do this already are the upcoming and available annotations, which return only the upcoming or currently available children of a brand.

To see all of the annotation we currently support in Atlas, check out the API explorer.

flexible resultset querying

We have three principles here:

  • The URL that represents the collection of all resources of the type you are looking for is used to query for resources of that type.
  • The queryable fields are the fields of the object you are querying.
  • Dotted notation is used to specify fields that are not top level.

In addition you may need to define additional parameters specific to API in question. For example, in Atlas we would also need to define common parameters for the following:

  • A way to restrict to a certain sort letter (not necessarily the first character of the title)
  • A way to represent querying by first broadcast
  • A way to restrict by different types of availability

Flexible querying is hard to implement, but in certain circumstances it can be hugely beneficial. These tend to be situations where you have an API with many different consumers, each with their own very specific needs.

Implementing flexible querying allows many different aggregations to be built on top of the same API, enabling clients to build directly on top of an API and not have to maintain their own indexes. In Atlas Deer we will be building this querying on top of an elasticsearch index.

reuse of common resultsets as subordinate resources

Whoa there, that sounds complicated. It isn’t really, but it is incredibly powerful. It’s probably best explained using an example.

As noted in the previous section, we intend to support flexible querying in Atlas. This will include querying of content. For example, the following would return all tv and film that is a drama and currently available:

/4.0/content?specialization=tv,film&genres=drama&available=true

But Atlas also supports topics. The topic for London is:

/4.0/topics/ccFk

We can get all of its content like this:

/4.0/topics/ccFk/content

But what if we want to restrict the content returned to only available tv and films that are drama, as before? Well because we’ve created a subordinate resource that represents the content for a topic, this is easy. We can make this content resultset act like any other:

/4.0/topics/ccFk/content?specialization=tv,film&genres=drama&available=true

This content resultset accepts all of the same query parameters and annotations as the normal /4.0/content and is a pattern that can be reused across the API. Anywhere there is a content resultset, it can be queried and filtered in the same way everywhere.

inline documentation

Lastly, it’s important for an API to be self-describing. Someone new to an API should be able to go to / and start exploring. If an API is self-describing, humans and machines can follow links to find related resources and documentation.

Don’t underestimate making an API human friendly. Every resource and every link to related resultsets should have a URI that resolves. That makes the API explorable in a browser and a pleasure to use as a developer.

In Atlas we’re going to implement this as an annotation. By specifying an documentation annotation, a description of the URI, it’s valid query parameters and any onward links will be added to the response.

get involved!

So there’s our view on modern API design, as well as some sneak previews of what will appear in the next major version of the Atlas API. Now we’d love to hear what you think.

Now is the time to influence what we do in API 4.0, so if you agree or disagree now is the time to shout. Let us know what you think in the comments and tell us the approaches you think are best for designing RESTful APIs.

blog comments powered by Disqus