Hypermedia APIs: The Benefits of HATEOAS

Almost 12 years ago, Roy Fielding introduced Representational State Transfer (REST) in his dissertation on Architectural Styles and the Design of Network-based Software Architectures. Since then, APIs adopting the REST architectural style (so-called "RESTful" APIs) have gradually increased in popularity. Nonetheless, a key constraint that Fielding proposed has yet to be adopted as a mainstream feature of RESTful APIs. This feature is known as Hypermedia as the Engine of Application State, or HATEOAS. APIs that enforce this constraint are referred to as HATEOAS-compliant APIs. More generally, APIs that adopt the characteristics of HATEOAS are called "Hypermedia APIs."

In this article we will examine the HATEOAS constraint through an example. We will design a traditional RESTful API, then augment the design so that it's HATEOAS-compliant. In the process, we will explain some of the key benefits of incorporating Hypermedia into the design of your RESTful API, as well as lay the groundwork for investigating Hypermedia API specifications (such as Siren, HAL and Collection+JSON) in later articles.

An Example API

The RESTful API we're designing in this article is for a public bookmarking service. Users can view a list of publicly available bookmarks. They can also filter and order this list based on certain criteria. For example, I can view the most popular bookmarks for a given user, or a list of bookmarks tagged as "video" or "meme." To fetch the actual URL of a bookmark, a user will need to send an additional request specifying the bookmark of interest. This way, requests for specific bookmarks can be tracked as "views"; this metadata can subsequently be used to calculate popularity.

In addition to bookmarks, our API provides basic user management functionality. This allows for bookmarks to be owned by users. A very primitive authentication scheme is used to allow destructive operations (deletes) on both users and bookmarks. Upon creation, a unique authentication token is generated and returned by the API. For bookmark deletion, this token must match the user who created the bookmark; for user deletion, the token must match the user being deleted.

Our API's Endpoints

Traditionally, we think of APIs in terms of endpoints. We can think of endpoints as a way to group the functionality we are providing through our API. In our case, it makes the most sense to expose two endpoints--one for bookmarks, and another for users. To formalize the requirements laid out in the prior section, we describe a series of operations for each endpoint below:


NameHTTP MethodURLPurpose
indexGET/bookmarksList all bookmarks
showGET/bookmarks/:idShow bookmark details (includes URL)
createPOST/bookmarksCreate a new bookmark
destroyDELETE/bookmarks/:idDestroy an existing bookmark


NameHTTP MethodURLPurpose
showGET/users/:idShow user details
createPOST/usersCreate a new user
destroyDELETE/users/:idDestroy an existing user

API Design Considerations

Now, as diligent API designers, we should think about practical use cases that combine multiple API calls. This helps us confirm that the API is useful to the developer of the client application. Moreover, it gives us an idea of the most requested operations. (This is important to know in case our API gets popular.)

Say a user wants to browse a list of recent bookmarks, then pick one and create his or her own bookmark using the same URL. The client app will first render a list of bookmarks to the user, allowing the user to pick one he or she would like to bookmark. When the user does so, the client application creates the bookmark (using the API) and renders the API response. In Ruby, this could look something like:

# Show list of bookmarks to the user
def list_bookmarks
  bookmarks = http_request('get', '/bookmarks', 'order=recent')

# Create a bookmark for a user
def create_bookmark(user_input)
  url = user_input['bookmark_url']
  headline = user_input['bookmark_headline']
  user = user_input['user_name']
  result = http_request('post', '/bookmarks', "url=#{url},headline=#{headline},username=#{user}")

# render [data] to user on a web (or mobile app) page (AKA "view")
def render(data); end

# send an http [method] request to [url], passing [parameters]
def http_request(method, url, parameters); end

NOTE: For simplicity, the render and http_request methods are left as stubs; the comments explain their assumed functionality.

In this use case, you may have noticed a shortcoming of our implementation. Specifically, we are not authenticating the user. As a result, any user could create bookmarks on behalf of another. Although a token-based authentication scheme was described earlier, let's assume that this was not considered a critical part of our MVP, so we launched the API without the authentication functionality.

Implementing the authentication requirement post-API launch would result in what's known as a "breaking change". As you'd expect, this occurs quite often with APIs, and requires coordination with API consumers (that is, developers who are implementing the API). For example, Facebook adopts a 90-day breaking change policy. On the consumer end, significant rework throughout the application stack is not uncommon. If workflows are affected, a complete rethink of the entire user experience may be necessary.

In the above example, the create_bookmark method would break, a new method for authenticating the user would be required, and an unauthenticated user of the client application will need to be restricted from certain functionality. Granted, the authentication oversight in our case would have been hard to miss. However, real-world APIs often provide richer functionality, so more subtle oversights can and do occur in practice.

The Hypermedia API "Value-Add"

A Hypermedia API is a means to address the breaking change issue. Instead of providing independent operations grouped by endpoint, a Hypermedia API is exposed as a Finite-State Machine (FSM). Each response type can be considered a state, while each operation is a transition.

In this way, the API consumer (and, by extension, the implemented client application) relies on the API to provide the control logic (that is, the workflow). As a result, any API changes that have an effect on the workflow would not break the client application (although the client application's behavior would change). This is arguably one of the key benefits of implementing a HATEOAS-compliant API.

Our API's Finite-State Machine

Here's how the state machine of our HATEOAS-compliant API might look:

Public Bookmarks API FSM

To keep things simple and thus easy to follow, we've left out the destroy user / bookmark functionality.


Initial StateInitial state of the client application*
User CreatedState reached immediately after the user has been successfully** created
Bookmark List ShownA list (index) of bookmarks is being shown
Bookmark ShownA single bookmark is shown, with all available details
Bookmark CreatedA user has successfully** created a bookmark

* Technically, this state is not part of our API. We have included it here to complete the FSM diagram.

** For simplicity, we have omitted failure states. However, it's conceivable for these to be defined with their own transitions. For example, a 503 HTTP response type (service unavailable) could provide / allow for a "retry" transition.


user createCreate a new user
bookmark indexView a list of bookmarks
bookmark index nextGet the next set (or "page") of bookmarks
bookmark index previousGet the previous set (or "page") of bookmarks
bookmark showShow a bookmark's details
bookmark createCreate a new bookmark

Key API Design Characteristics

As you can see in the above diagram, a key feature of Hypermedia APIs is to limit the possible transitions for each state. In particular, users must pick a bookmark from a list to view its details (the bookmark show transition is only possible from the Bookmark List Shown state). Similarly, no functionality is available until a user is created. (The User Created state must have been visited prior to any of the other states.)

Thus, our Hypermedia API is the engine of application state; in other words, it is a HATEOAS-compliant API.


We hope that this article has inspired you to further discuss, investigate and experiment with Hypermedia APIs. In future installments we will continue to develop our Public Bookmarks API. In particular, we will investigate how some of the emerging Hypermedia API standard specifications (such as Siren, HAL, and Collection+JSON) can be applied to our API.

By Paul Sobocinski. Paul is a full stack engineer who's been developing in web applications for the past ten years. He can be found on Twitter and Google+.

Be sure to read the next REST article: What HTTP/2 Means for REST APIs


Comments (1)


Should Authentication be HATEOAS driven, or out of band? 

Specifically, I'm investigating an architecture style where the unauthenticated home page has a link to log in, which redirects to an oauth service, which upon authentication hits a return address with a token appended to it. This means the application is now in the "authenticated" state, and all subsequent links from here contain the oauth token in the auth_token URL query parameter. Using this method, the oauth token is never stored persistently anywhere (yay securiety), goes away implicitly when the client exits, and HATEOAS removes all authentication responsibility from the client - the client just follows links and presents an oauth form to the user when necessary.

However, servers and browsers often cache or log GET request URLs, meaning this token could fairly easily leak, and the Authorization request header is an HTTP standard for this and other reasons. But putting the oauth token in the Authorization header requires out of band information -- namely, the knowledge that the oauth token must be included in subsequent requests -- and for session state (the token) to be kept on the client. So then this puts responsibility on the client and is no longer HATEOAS compliant.

A third option I've explored is to extend the hypermedia API to make request headers explicit in links, but I've not seen this practice widely adopted.

Hypermedia APIs are still evolving, so is there a best practice or existing de facto standard here?