1.12 HATEOAS

In this tutorial, we'll learn about an important concept relating to REST APIs: HATEOAS

Notes

That's not a typo. This is actually an acronym. HATEOAS. In the running for the worst acronym ever. It stands for Hypermedia as the Engine of Application State. I know. Worst acronym ever, huh? But bear with me, and you'll soon understand what that means.

Hyperlinking

Maybe you remember, I mentioned in the first video of this series that there's no service definition specification for REST APIs. There's no formal document that really documents the API. Most REST APIs have "help" pages that explain what the API URIs are and what operations are supported. I also mentioned in the first video that the best RESTful APIs don't even need any documentation. I'll now explain what I mean by that.

So, we visit websites online all the time. When was the last time you looked up any documentation to use a website? Well, never, I hope. You don't need documentation to use web sites. You go to the home page, and you'll find links to other pages. You click on one such link, and you'll get that page, with more links. You don't need to read a document to know where to go. You just remember the website home address, and any other links you need to navigate will be provided to you in the response.

This is basically the advantage of using HTTP. Remember that HTTP is HyperText Transfer Protocol. We've discussed that hypertext is text that has links to other text. These links, which are called hyperlinks, are what's really handy to navigate your way through any site. Let's think about the response we return in our REST API. What if we implement the same concept there too? Let's say you receive a GET request from a client for a message ID. We return the message information in JSON or XML, yes. But what you could also do is send links to comment resource URIs. And likes and shares resource URIs. It's the server saying "Hey client, I know you asked for message ID 20. Here's the contents of message #20. I'm also throwing in collection resource URIs for comments, shares and likes. If you want to get a list of all the comments for message ID 20, this is the URI to use. Oh, and here's the profile resource URI for the author of the message, if you want to get the profile information of the author of this message".

So, the web service is being super-helpful to the client by providing all these links in the response. Similar to hyperlinks in web sites. Whether the client wants to use it or not doesn't matter. But if they want it, it's there. And just like that, you've eliminated the need for documentation for all these APIs. The client developer just picks up the value of the right URIs from a previous response and makes subsequent calls to those URIs.

If you do this, you don't let the client programmer have to know and hard-code the URIs in order to interact with the resources and the application state. You basically let the hypertext you send in the response drive the client's interaction with the application state. So, you could say that hypertext, or hypermedia as it is sometimes called, is being the driver or engine of application state. Hypermedia as the Engine of Application State. HATEOAS. Whew. Does that make sense now? It's still a bad choice for a name. But it should at least make some sense now.

A Scenario

Let's walk through a scenario so that this concept becomes clearer. Let's start with the /messages collection URI. Accessing /messages should give you a list of messages in the system. Let's say a message representation has the following fields:

  1. Message ID
  2. Message Content
  3. Message Author
  4. Posted Date

Four simple properties. A JSON representation for a sample message would look something like this:

{
  "id": "01",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014"
}

Now when you access the /messages collection URI, you'd basically get a collection of such message resources. To keep it simple, let's say there are just 3 messages in the system. Accessing /messages would give something similar to this:

[
  {
  "id": "1",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014"
  },
  {
  "id": "2",
  "content": "Yo!",
  "author": "sid",
  "postedDate": "04-01-2014"
  },
  {
  "id": "3",
  "content": "What's up?",
  "author": "jane",
  "postedDate": "04-02-2014"
  }
]

Now that the client has the list of messages, let's say they want the details of the first message - message ID 1. We've already designed the resource URI for message to be /messages/{messageId}. So, to get the URI, they'll have to take the value of the ID field of the message they are interested in, and append it to the string /messages/ and there they have the resource URI. But this means that the client will have to know this beforehand. They need to know that they need to pick up the ID property from the response, and they need to know what to append it to. Now, here's a question. As a API service implementer, why not send that to the client yourself? Since we are sending the message resource details anyway, why not just construct the URI fully and send it to the client?

Consider a sample response for a single message like this:

{
  "id": "1",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014",
  "href": "/messages/1"
}

If this were to be the kind of response for every message in /messages, then the client wouldn't really have to do any URI construction. The resource URI is one of the properties of the resource. If you were to design your API so that every resource has the instance resource URI to itself, it makes it really convenient for the client to use it.

Notice that the name of the link property is href. That must be familiar. That's exactly how you specify links in HTML. href is a property of the <a> tag. It serves a similar purpose here.

We are on our way to implementing some HATEOAS concepts. We are not fully there yet. There are still some more things you'll need to learn. Let's look at the concept of links, and how you can apply them to the resources in the Messenger API. We've looked at adding the resource URI to every resource. So, a profile resource, or a comment resource, well, pretty much every resource could have a href attribute that has the value of the instance resource URI. Bu that's not the only link you can provide. For instance, a message resource could also have links to get all the comments for that message. And all the likes and shares for that message. You could even have links for the client to post a new comment to that message. Keep extending this, and it gets a bit messy.

{
  "id": "1",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014",
  "href": "/messages/1",
  "comments-href": "/messages/1/comments",
  "likes-href": "/messages/1/likes",
   "shares-href": "/messages/1/shares",
   "profile-href": "/profiles/koushik",
   "comment-post-href": "/messages/1/comments"
}

If you do this, the client doesn't need to remember the URIs, yes, but they now have to remember the property names for these URIs and you basically have then same problem. There needs to be a better way to manage these links. And there is! You can use the rel attribute.

If you've used the anchor tags when writing HTML, you might have encountered this rel attribute before. It's basically an attribute that you can add to any link to specify the relationship between the current document and the linked document.

The most common example of rel is in stylesheet links. You'd have seen stylesheet links in HTML head tags like this:

<link rel="stylesheet" href="path/to/some.css"/>

Here href provides the actual URL being linked, and the rel attribute describes the relation of that link to the main document. Here the relation is that the link is a stylesheet of the main document.

We can use the rel attribute to add extra information in the links in our REST response. Here's the original href response modified with the rel attribute addition:

{
  "id": "1",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014",
   "links" : [
                {  
                    "href": "/messages/1",
                    "rel": "self"
                }
            ]
}

What's different here is that we've introduced this new property called links which is an array. This is going to contain all the links that you'd want to embed in the response. However, you add the rel attribute to make it clear what the link points to. Notice the rel value self which indicates that the link in the resource points to itself.

This could be extended by adding new links and assigning the appropriate rel values for each:

{
  "id": "1",
  "content": "Hello World!",
  "author": "koushik",
  "postedDate": "03-01-2014",
   "links" : [
                {  
                    "href": "/messages/1",
                    "rel": "self"
                },
                {  
                    "href": "/messages/1/comments",
                    "rel": "comments"
                },
                {  
                    "href": "/messages/1/likes",
                    "rel": "likes"
                },
                {  
                    "href": "/messages/1/shares",
                    "rel": "shares"
                },
                {  
                    "href": "/profiles/koushik",
                    "rel": "author"
                }
            ]
}

Now the client doesn't need to remember the link property values. They just have to find the link with the right rel value for the resource they want and then look up the href value from that link.

A couple of things to note here. While the concept of having the URIs in the response to achieve HATEOAS is something that's well understood and mostly agreed upon by all, the way to do this could vary differently among implementations. The format of JSON that I've outlined here is just one of the multitude of ways you could structure links. Again, there's no right or wrong. You can choose to tweak how you want to present the links in the JSON response of your API depending on your preference. Secondly, the rel attribute is a part of the HTTP specification, so there are only certain standard values that are allowed for it. This link lists the available values. And obviously, the rel values here like "comments" and "likes" are not valid. But we'll still use it. Like I mentioned before, the idea is to have an API that's easy for the clients to use, and easy for you to maintain. You don't want to focus too much on getting things right and going by the book. At least, not at the cost of complicating the API too much.

In summary, HATEOAS is a way to provide links to resources in the API response, so that the client doesn't have to deal with URI construction and business flow. They make a request, and the next steps, along with the URIs are handed to them in the response. When you write APIs, you can choose to add URIs in the response using the href attribute. You can also provide more information about the relationship of the linked resource using the rel attribute.