What is HATEOAS?

HATEOAS stands for Hypermedia As The Engine Of Application State. It is a technique that is often used in conjunction with REST APIs.

The basic idea of HATEOAS is that when your API returns a resource, the representation should include links that allow the consumer of the API to discover resources related to the current one. It’s similar to the way websites work. When people open a web page, it contains links to further pages, and they can use these links to navigate without any other information.

Let’s see a simple example where we are getting back the details of a user:

{
    "id": 19,
    "name": "JohnDoe",
    "email": "john.doe@gmail.com",
    "links": [{
            "href": "/users/19/comments",
            "rel": "comments",
            "method": "GET"
        },
        {
            "href": "/users/19",
            "rel": "delete",
            "method": "DELETE"
        }
    ]
}

As you can see we have a links array, which contains two links. One for getting the comments made by the user, and one for deleting the user. Because these links are provided, the consumer of the API do not need to know them. It only has to know where to find them. And of course, it needs to know the possible actions about a resource. In this case, it needs to know in advance that a user has comments and that it can be deleted, otherwise, the rel attribute wouldn’t make too much sense to the client.

This is not the only syntax that can be used for providing the links, but it’s a fairly standard one.

Here is a more real life example from the PayPal API:

{  
  "links": [{
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832",
    "rel": "self",
    "method": "GET"
  }, {
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
    "rel": "refund",
    "method": "POST"
  }, {
    "href": "https://api.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
    "rel": "parent_payment",
    "method": "GET"
  }]
}

Why use HATEOAS?

The biggest advantage of this approach is that consumers of the API do not have to hardcode the API URLs into their code. They just have to know where to find them. This allows the API developers to freely change the URL structure as it won’t break the clients.

Limitations

In an optimal case, HATEOAS would allow a consumer to start from a “root” resource and just by going through the links, discover the URL for any endpoint and use it. To an extent, it can work. For example, if we are trying to get the likes received by a user for a specific post, a client could automatically

  • GET the user
  • GET the posts of the user
  • GET the specific post of the user
  • and finally, GET the likes for that specific post

All this based on the links returned in the response for each request.

This approach would not work if we need some extra information to be able to submit the request. For instance, if I want to create a post for a user, I need to know what fields do I have to submit. I can get the target URL from the response, but getting all the information about the fields would be pretty tricky. Even if I would get back the list of fields in the response, I would need to have a logic in place that can determine the correct value for each field.

Summary

The HATEOAS technique can be very useful if we are not trying to overuse it. It’s great when we would like to automatically discover content/endpoints to decouple the clients from the API. But we should not try to construct an API that is fully describing itself using this technique because in most cases, this would not be possible in a dynamic way.

What is REST?

REST stands for Representational State Transfer. 

It is an architectural style (or software design pattern) that is used for designing web applications or web services. Well, that is not entirely true, because REST can be used in case of any networked application, but because the overwhelming majority of these are web applications/services, we are going to refer to them in this manner.

When you are using REST to build a web service, you can say that it is a RESTful web service. If you are referring to the API that this web service provides, it’s a RESTful API.

REST Guidelines

REST does not have a specification or concrete rules on how it must be implemented. It is a collection of guidelines that you can use if you would like to develop a RESTful API. You can choose to follow all or just a subset of the guidelines depending on how RESTful you want your API to be.

This approach might become more clear if we compare it to the SOAP communication protocol that is also a commonly used in case of web services. This protocol uses XML format for communication. The request/response formats need to follow strict rules and there is a separate descriptor language called WSDL to describe the formats used by the web service. REST, on the other hand, does not tie your hand regarding what format you should use (XML/JSON, etc.), what should the request or response contain. It is just a set of guidelines to use when building your web service. Although it’s worth mentioning that JSON is the most commonly used format.

So let’s see what are the guidelines that we need to follow if we would like to implement a RESTful web service:

  • Objects as resources: Server objects are treated as resources and are exposed using a uniform URL structure and are manipulated using a common set of verbs (HTTP request methods).
  • Well chosen HTTP status codes
  • Stateless client-server communication
  • Representations: Representations of resources are not dependent on which resource we would like to get, but it is determined by the request (with the use of request headers).
  • Cacheable resources

Objects as resources

In regular web applications that you can access through your browser, you can usually see URLs that refer to some action like:

  • /users/show-profile
  • /results/processResults.php
  • etc.

As you can see these describe some kind of action, they tell the server what it needs to do.

REST has a quite different approach to this. It does not talk about actions, it talks about resources. Objects on the server side (e.g. blog posts, users etc.) are treated as resources, and you can reference them using a standard URL format. The operation you would like to perform on a resource is determined by the HTTP method that you use.

The purpose of the HTTP methods are:

  • GET: Retrieve a resource.
  • POST: Create a resource.
  • PUT: Update a resource.
  • DELETE: Remove a resource.

We can differentiate instance and collection resources depending on whether we are talking about one or more objects. Let’s see some examples of the standard URL structure and the HTTP methods that we can use.

URLHTTP methodDescription
/messagesGETReturn all the messages.
/messages/{messageID}GETReturn a single message.
/messagesPOSTCreate a new message.
/messages/{messageID}PUTUpdate a message.
/messages/{messageID}DELETEDelete a message.
/messages/{messageID}/likesGETReturn all the likes for a particular message.
/messages/{messageID}/likes/{likeID}GETReturn information about a specific like for a specific message.
/messages/{messageID}/likesPOSTCreate a new like for a particular message.
/messages/{messageID}/likes/{likeID}PUTUpdate a like for a particular message.
/messages/{messageID}/likes/{likeID}DELETEDelete a like for a particular message.
/messages/{messageID}/likesDELETEDelete all likes for a given message.

As you can see, some of the URLs are identical, but we are using a different HTTP method, so the server will be able to decide what we would like to do.

HTTP method idempotence

We can say, that an HTTP method is idempotent if issuing the same HTTP request multiple times results in the same outcome as if we have just issued it a single time.

The HTTP methods GET, PUT, DELETE are idempotent, if used properly. Let’s see why:

  • Issuing a GET request multiple times will just return the same response multiple times. No issue is caused by this, it can be easily seen. Of course, if the data on the server changes in the meantime, a different result set will be returned.
  • If you issue multiple PUT requests, you are updating the resource the same way multiple times to the same state. The end result will be the same as if you would have just made one call.
  • Making a DELETE request multiple times won’t cause issues because the requests other than the first one won’t find the resource and will not be able to delete it. So the end result is the same as if there were only one call.

On the other hand, POST is not idempotent as making multiple POST requests with the same content will result in the creating of duplicate resources (with different IDs).

There is catch, however. For the above to be true, you have to design your API correctly. Here are some examples that would violate these idempotence rules and should be avoided when designing a RESTful API:

  • You have a GET endpoint that creates a resource. GET should be idempotent, but this case it is not.
  • You have a PUT endpoint that allows users increase the amount of votes for a picture. PUT should be idempotent, but in this case it is not.
    • Because you increase the votes by incrementing the current vote count, issuing this request multiple times will cause the votes to increase multiple times. Possible solutions:
      • Use POST in a RESTful way by creating a new “vote” object every time a user votes.
      • Use the less known PATCH HTTP method (not the scope of this tutorial).

Through a final example, you’ll easily see how important it is to keep to these rules. When you are submitting a form using a POST request and try to refresh the browser, you are presented with a message asking if you are sure that you want to issue the same request again. This is because of the fact that POST is not idempotent and issuing it multiple times might cause unwanted side effects like buying two products instead of one. For other HTTP methods, there is no such warning, because they are supposed to be idempotent.

Well chosen HTTP status codes

Becuase REST APIs are consumed by computers and not humans, it is important to always use the proper HTTP status code in the response, so the consumer code is able to decide if the request was successful or not. If the operation failed, the status code can tell some details about the reason of the failure and the client code can use that to decide what should it do (e.g. retry the request).

Stateless client-server communication

In case of REST, the distinction between the client and server is important. These need to be independent of each other, so we can easily scale or relocate them to different servers without affecting the other.

Also, REST relies on a stateless protocol (that is almost always HTTP). This means that every request should contain every information that is needed for the server to process it, so the requests must not depend on anything that was sent by a previous request.

If we take this seriously, we cannot even store anything in the session in a RESTful application, the client has to track this kind of information.

In case of APIs that does not require authentication, this is easily feasible. Most REST APIs, however, use some form of authentication. In this case, no storage on the server side is needed if the clients send their credentials with every request. However, it is not always the best way because in this case we always need to re-authenticate the user. Usually, there is some kind of token generated that the server accepts as authentication for a given amount of time. This slightly violates the stateless principle but some sacrifices have to be made if we would like to make a properly working system.

Representations

Each resource (e.g. blog post) can have many different representations. The most common ones are JSON and XML. REST says that the returned representation of the resource is not dependent on which resource we would like to get back, but on the HTTP request, we send. The request should contain information about what representation we would like to get back.

The headers used are:

  • Content-Type: The client and the server both use it to specify the format of the data that is being sent.
  • Accept: The client uses this to specify what format it expects from the server.

Cacheable resources

In REST, resources have to be explicitly declared cacheable or not. Caching a resource can eliminate some of the calls to the server as we can just use the locally cached version.

Documenting a REST API

The first step to a good documentation is to follow the REST principle as much as possible. This will allow users to more quickly understand how they can use your API. Of course, you have to create a documentation as well. There are a lot of various ways how you can structure the documentation, here is one example:

  • Description of the endpoint
  • URL
  • HTTP method
  • URL parameters
  • Data parameters (in request body)
  • Request / response format
  • Success response
  • Error responses
  • Sample call
  • Notes

REST in the real world

When we refer to REST, it can mean different things. As an example, it can be an API that fully conforms to the REST principles or it can be a web application that only uses some ideas from REST.

For example, we could just have some endpoints in a web application that use the REST URL structure and follows the HTTP method usage guidelines but uses the session to store details about the currently logged in user. This obviously violates the stateless principles, because we are depending on data stored on the server side.