REST – What Does HATEOAS Offer for Discoverability and Decoupling?

hateoashttprest

Lately I've been reading about Hypermedia as the Engine of Application State (HATEOAS), the constraint that is claimed to make a web API "truly RESTful". It boils down to basically including links with every response to the possible transitions you can make from the current state.

Let me illustrate what HATEOAS is based on my understanding – and please do correct me if I missed something.

/
    GET: {
        "_links": {
            "child": [
                { "href": "http://myapi.com/articles", "title": "articles" }
            ]
        }
    }

/articles?contains=HATEOAS
    GET: {
        "_items": [
            { "uri": "http://myapi.com/articles/0", "title": "Why Should I Care About HATEOAS?" },
            { "uri": "http://myapi.com/articles/1", "title": "HATEOAS: Problem or Solution?" }
        ],
        "_links": {
            "self": { "href": "http://myapi.com/articles", "title": "articles" },
            "parent": { "href": "http://myapi.com/", "title": "home" }
        }
    }

    POST: {
        "title": "A New Article",
        "body": "Article body",
        "tags": [ "tag1", "tag2" ]
    }

/articles/0
    GET: {
        "title": "Why Should I Care About HATEOAS?",
        "body": "Blah blah blah"
        "tags": [ "REST", "HATEOAS" ],
        "_links": {
            "self": { "href": "http://myapi.com/articles/0", "title": "article" },
            "parent": { "href": "http://myapi.com/articles", "title": "articles" }
        }
    }

HATEOAS is claimed to provide two major benefits:

  1. The entire service is discoverable starting form the root URI, documentation is no longer needed.

  2. The client is decoupled from the server which can now change the URI structure freely. This eliminates the need for API versioning.

But in my view, a service is a lot more than its URI structure. To use it effectively, you also need to know:

  • what query parameters you can use and their possible values
  • the structure of the JSON/XML/whatever documents you need to send in your POST/PATCH/etc requests
  • the structure of the response sent by the server
  • the possible errors that might occur

Based on the above, HATEOAS only solves a tiny fraction of the discoverability and coupling problems. You still need to document the above four aspects and clients will still be strongly coupled to the server because of them. To avoid breaking clients, you still need to version your API.

The only benefit it provides is that you can change your URL structure more or less freely (by the way, what happened to the principle "Cool URIs don't change"?). Is my understanding correct?

Best Answer

I think your instincts are largely correct; those proclaimed benefits really aren't all that great, as for any non-trivial web application the clients are going to have to care about the semantics of what they're doing as well as the syntax.

But that doesn't mean that you shouldn't make your application follow the principles of HATEOAS!

What does HATEOAS really mean? It means structuring your application so that it is in principle like a web site, and that all operations that you might want to do can be discovered without having to download some complex schema. (Sophisticated WSDL schemas can cover everything, but by the time they do, they've exceeded the ability of virtually every programmer to ever understand, let alone write! You can view HATEOAS as a reaction against such complexity.)

HATEOAS does not just mean rich links. It means using the HTTP standard's error mechanisms to indicate more exactly what went wrong; you don't have to just respond with “waaah! no” and can instead provide a document describing what was actually wrong and what the client might do about it. It also means supporting things like OPTIONS requests (the standard way of allowing clients to find out what HTTP methods they can use) and content type negotiation so that the format of the response can be adapted to a form that clients can handle. It means putting in explanatory text (or, more likely, links to it) so that clients can look up how to use the system in non-trivial cases if they don't know; the explanatory text might be human readable or it might be machine readable (and can be as complex as you want). Finally, it means that clients do not synthesise links (except for query parameters); clients will only use a link if you told it to them.

You have to think about having the site browsed by a user (who can read JSON or XML instead of HTML, so a little weird) with a great memory for links and an encyclopædic knowledge of the HTTP standards, but otherwise no knowledge of what to do.

And of course, you can use content type negotiation to serve up an HTML(5)/JS client that will let them use your application, if that's what their browser is prepared to accept. After all, if your RESTful API is any good, that should be “trivial” to implement on top of it?