Tag Archives: Architecture

How to make RESTful APIs flexible and sensible to work with

I have been an advocate for RESTful APIs since I first came across the concept in 2010, and although it took me a while to wrap my head around the concept, I knew from the first moment that this was something that made sense to me compared to the SOAP based APIs that I had previously been working with. Though initially starting out with RESTful APIs in their purest form (so to speak), I have discovered a couple tricks which make them a lot easier to work with, and in this post I’ll share these with you.

Fundamentals of RESTful APIs

First of all, let’s make sure that we have the same basic understanding of the concept of a RESTful API. It’s an API which uses:

  • A base URI, e.g. https://path.to.api.
  • Standard HTTP methods, i.e. POST; GET; PUT; PATCH; and DELETE to request and manipulate data.
  • URLs which make it (potentially) easy for a human to interpret which information is being requested, e.g. GET https://path.to.api/v1/users/1, i.e. get information about the user with ID 1.
  • Standard HTTP status codes as high-level responses to requests, e.g. 200 OK; 201 Created; 401 Unauthorized; 403 Forbidden etc. which makes the overall status of a request easy to interpret.
  • An internet media type for data, e.g. JSON.
  • Autonomous requests from a client (API consumer) which contain all of the information required for the API to process the request, i.e. the API doesn’t need to retain the state of the client.

The common denominator here is: Standards, i.e. a RESTful API is built using already existing standards, rather than having to invent public classes which are specific to a said project.

Response types

With a RESTful API you have the following types of responses:

  1. Resource: A resource is an extensive object containing all information regarding the resource, e.g. GET https://path.to.api/v1/users/1 should result in a response containing all available information about the user with ID 1.
  2. Collection: A collection is a list of a specific kind of resource, e.g. GET https://path.to.api/v1/users should result in a response containing a list references to all available user resources.

Basically, what this means is that is that if you wanted information about all of the users in the system (just to stick with the example), you would first:

GET https://path.to.api/v1/users

[
    "https://path.to.api/v1/users/1",
    "https://path.to.api/v1/users/2",
]

… to get the entire array/list (collection) of users, and then get the full details for each user (resource) in the collection as per the following requests:

GET https://path.to.api/v1/users/1

{
    id: 1,
    name: "Firstname Lastname"
}
GET https://path.to.api/v1/users/2

{
    id: 2,
    name: "Givenname Surname"
}

… to get the information regarding each resource.

Improvements

As REST is a conceptual approach to building an API, each development team has to take it from here and build something which suits their needs. In the remainder of this post, I will be examining various approaches which I have found useful in the projects which I have worked on.

Basic versioning

As you may have noticed the examples above all contain v1 as part of the request URL, i.e. an abbreviation for “version 1”. The reason behind including this is simply to avoid breaking functionality in the case that your team decides to build a new API from scratch, e.g.:

At some point you should probably get rid of the v1 API for maintainability reasons, but by using versioning you can build the new version alongside the old without constantly having to stress over having to make sure that the consumers of the API, e.g. external users, are in sync with your progress.

Flexible collections

Instead of merely having collections being a list of resources, I suggest structuring the response into an object, which includes meta-data about the collection and where most of the meta-data properties can be given values as part of a request thus making the collection more flexible to work with, i.e. (numbered for convenience):

  1. Items: An array of resource objects.
  2. Sort: The property name in the individual resource objects by which the whole collection of data is sorted, e.g. {sort: 'name'} or {sort: 'id'}, both before being sent as a response from the API and as sorted in the items array.
  3. Reverse: A property that lets you reverse the direction by which the whole collection of data is ordered, i.e. {reverse: true}, before being sent as a response from the API. In my opinion, the default should be ascending, and thus applying the true value to the property should return the collection in descending order.
  4. Limit: The maximum number of resource object in the items array, in case you only wish to include a subset of the whole collection as part of the items array, e.g. {limit: 25} will include a maximum of 25 resource objects regardless of the length of the whole collection.
  5. Offset: The offset in the whole collection of data from which the limit is calculated. When used together with limit, it makes it possible to incorporate paging of the response data.
  6. Previous: A link to the previous element in the collection (if applicable).
  7. Next: A link to the next element in the collection (if applicable)
  8. Total: The total number of resource objects for the request regardless of how many are included in the items array, so that you always know the size of the total data-set.
  9. Details: The detail level of each resource object in the items array, e.g. specified by a string, for instance all. A common approach in REST is to have each resource in a collection contain a minimum of data, e.g. a URL to where the complete set of data for the resource can be acquired, but I have found that sometimes it makes more sense to get all information regarding the resources as part of the collection instead of as multiple subsequent requests.

The following request:

GET https://path.to.api/v1/users?sort=name&reverse=true&limit=10&offset=50&details=all

… should thus result in the following response (please note that the individual resources in the items array have been left out):

{
    items: [],
    sort: 'name',
    reverse: true,
    limit: 10,
    offset: 50,
    previous: '/v1/path.to.previous',
    next: '/v1/path.to.next',
    total: 200,
    details: 'all'
}

Examples

Get all users sorted by name:

GET https://path.to.api/v1/users?sort=name

Get all users sorted by name in reverse order:

GET https://path.to.api/v1/users?sort=name&reverse=true

Get the 25 first users:

GET https://path.to.api/v1/users?limit=25

Get all users except the first 25:

GET https://path.to.api/v1/users?offset=25

Get users 25-49:

GET https://path.to.api/v1/users?limit=25&offset=25

Get the total number of users without any additional information for each resource:

GET https://path.to.api/v1/users?limit=0

Advertisements