Web API Definition Guidelines

Web APIs are patterns of HTTP requests/responses used to interact with a remote system.

APIs are meant to be used by programmers and for this reason they needs to be easy to understand and use as well as intuitive. Moreover, programmers will come with inherited knowledge base about what APIs looks like and what they do and they do not wants to learn a complete new language just for testing few new functionalities but they will most likely move to another technology that is more friendly and easy to use.

REST vs RESTful?

REST is the name given to the architectural pattern that defines means for clients and servers to exchange data. It is defined in a PhD disseration by Dr Roy Fielding in 2000 (but it is not a standard).

As such, REST defines constraints on the elements of the architecture, for example:

  • A REST application have a server that manages application data and state. The server communicates with a client that handles the user interactions.

  • Servers don’t maintain any client state. Clients manage their application state. Their requests to servers contain all the information required to process them.

REST is the architectural pattern proper of HTTP. It was defined after the definition of HTTP that’s why the terms are misinterpreted to be the same.

When we talk about RESTful we refer to services that are based on REST architecture style.

RESTful is not clearly defined even if it uses defined standards such as HTTP, URI and JSON.

Introduction

In this article, I am trying to summarise the best practices that have been captured in two books by a leader player in this field:

Therefore I will make extensive use of their examples.

We need to remember that REST APIs follow a data-oriented approach that focus on the objects they interact with (rather than the function to manipulate such objects).

REST API focuses on the object they expose rather than the functions to manipulate the object. So for instance when you ask for an object you can refer to the objects as /objects (e.g., /dogs). In contrast you can have function-oriented API e.g., /getDogs.

The link 0

When defining APIs, it’s always necessary to publish at least one well-known URL. This is the minimum amount of URL that a developer needs to know in order to get started.

It should be possible to explore the APIs only by using this one URL.

When you request a URL you will receive a representation of the resource requested. The most supported languages to represent a resource is JSON.

If you request “https://dogtracker.com” you will receive it’s representation as:

    {
        "self": "https://dogtracker.com/",
        "kind": "DogTracker",
        "persons": "https://dogtracker.com/persons",
        "dogs": "https://dogtracker.com/dogs"
    }

The representation contains a URL to itself and other URLs to other collections of objects (dogs and persons in this example).

When querying a collection you can receive its representation as:

    {
        "self": "https://dogtracker.com/dogs",
        "kind": "Collection",
        "contents": [
            {
                "self": "https://dogtracker.com/dogs/12344",
                "kind": "Dog",
                "name": "Fido",
                "furColor": "white"
            },
            {
                "self": "https://dogtracker.com/dogs/12345",
                "kind": "Dog",
                "name": "Rover",
                "furColor": "brown"
            }
        ]
    }

The list of objects inside of the “contents” its a mere list of the objects requested.

It’s common to name a collection with the plural name of the requested objects. E.g.: the collection “dogs” represent the a collection of “dog” objects.

To query a collection for a particular object, it is traditionally done following two different approaches:

This structure enables a simple and intuitive tree navigation that expresses relations with other objects, e.g.: https://example.com/persons/12345/dogs/09876 Without saying anything this URL we are requesting the dog 09876 among the ones linked to person 12345.

Furthermore, because many application developers consuming the API find the first style more readable and intuitive. API developers implementing the API usually find the first style easier to implement. For many people, the first style of query URL does not imply a commitment to a comprehensive query capability, but they are more likely to interpret the second style of query URL to mean that all combinations of query parameters and values are supported. This style of query URL can easily express graph traversal queries that are difficult to express in the query parameter style.

Many times instead, it is difficult to request objects following a hierarchical structure. In that case you can use:

https://example.com/persons/5678/dogs?color=red&state=running&location=park



Identify a resource

All resources needs to have a link that can be saved and stored by the client to be used later on therefore it must be as much stable as possible.

For example, resources can be identified by human-friendly identifiers as: https://dogtracker.com/person/JoeMCarraclough It is clear that if the person requested change name (and therefore its URL) all the reference will break.

Resources can be also identified by machine-generated identifiers: https://dogtracker.com/persons/e9cdcf7a-25b3-11e5-34363bd0ac10 In this case the URL is not human-friendly not ideal for developers.

A good solution is to use machine generated URLs to identify a specific resource and give to that resource an alias property human-friendly that points to it. E.g.:

    {
        "self": "https://dogtracker.com/person/e9cdcf7a-25b3-11e5-34363bd0ac10",
        "id": "e9cdcf7a-25b3-11e5-34363bd0ac10",
        "kind": "Human",
        "name": "LasJoeMCarraclough",
        "alias": "https://dogtracker.com/persons/JoeMCarraclough",
    }

When referring to this resource you have to use the permanent link, e.g.:

    {
    "id": "12345678",
    "kind": "Dog",
    "name": "Lassie",
    "furColor": "brown",
    "owner": "https://dogtracker.com/persons/e9cdcf7a-25b3-11e5-34363bd0ac10"
    }

The permanent link to the person is https://dogtracker.com/persons/e9cdcf7a-25b3-11e5-34363bd0ac10. This link implies that the kind person cannot change. This is reasonable assumption for humans, but it might be not true for other entities.

Resource fields

A good example of resource is:

    {
        "self"   : "https://dogtracker.com/persons/34363bd0ac10",
        "id"     : "e9cdcf7a-25b3-11e5-34363bd0ac10",
        "kind"   : "Human",
        "name"   : "LasJoeMCarraclough",
        "alias"  : "https://dogtracker.com/person/JoeMCarraclough",
        "dogs"   : "https://dogtracker.com/persons/34363bd0ac10/dogs",
        "actions": "https://dogtracker.com/persons/34363bd0ac10/actions"
    }

Including a kind property helps clients recognize whether or not this is an object they know how to process.

Including a self property makes it explicit what web resource’s properties we are talking about without requiring contextual knowledge. E.g.: the resource can be retrieved by directly performing a GET request.

Including a actions property is needed to modelling the action that the resource can perform. A POST request can be performed to request a specific action on a resource followed by the details of such action. It is possible also to specify the actions that can be performed to a resource with different fields, e.g.:

    {
        "pauseRequests": "https://dogtracker.com/persons/34363bd0ac10/actions",
        "stopRequests" : "https://dogtracker.com/persons/34363bd0ac10/actions"
    }

Note that all the action fields point to the same URL. 

The name of the fields can follow either camelCase or snake_case equivalently but they need to be consistent.

When an object representation contains too many fields it is possible to request only a subset of them as:



Adding, removing, changing resource

When you model your URIs after resources and use HTTP verbs you make your API predictable. Once developers know how you defined your resources, they can almost predict what the API looks like.

In REST, adding a resource is usually expressed with a POST to the collection that have these objects, e.g.:

POST https://dogtracker.com/persons/34363bd0ac10/dogs

This request indicates the intention to add a dog to the person 34363bd0ac10.

A GET request is used to express the intention of retrieving the resource details.

A PUT request is used to completely replace the resource we are pointing.

A PATCH request instead is used to alter the value of a particular field.



What to do for versioning?

We all understand that at some point in time, APIs will change.

If you can change your API in a backward-compatible ways then this is the way to go.

When changes are breaking the backward-compatibility then you might want to add a version in the requesting URL as:

https://dogtracker.com/v2/persons/98765432

When requesting such URL, the representation returned can include the version as well:

    {
        "self"   : "https://dogtracker.com/v2/persons/34363bd0ac10",
        "id"     : "e9cdcf7a-25b3-11e5-34363bd0ac10",
        "kind"   : "Human",
        ...



Error handling

An API is a shield that hide the computation performed behind it. Remember that programmers learn by trial and error, therefore the importance of messages is extremely valuable.

For example, when a request is created the response can include the Location header to express where is it located, e.g.

    HTTP/1.1 201 Created
    Location: https://dogtracker.com/dogs/1234567

When a method is not permitted:

    HTTP/1.1 405 Method Not Allowed
    Allow: GET, DELETE, PATCH

Learning the combinations of status codes with their matching headers and the scenarios in which they are used is important. A programmer can use her knowledge of the web to navigate throw the errors to succeed.



Additional Resources on REST