RESTful Web Services for Resource-Oriented Architectures (ROA) – Part 2 – HATEOAS

In my last post on RESTful web services for Resource-Oriented Architectures, I focused on Addressibility and the URI of the resources.1 In this article, I’ll tackle Connectedness and its relationship to the REST constraint of Hypermedia as the Engine of Application State (HATEOAS).2

In the book RESTful Web Services, HATEOAS is described by Richardson and Ruby as “Connectedness”. However, Roy Fielding says the property of connectedness is just a step towards using hypermedia to drive application state.3 In addition to providing links in our resource representations to related resources, HATEOAS is “about late binding of application alternatives that guide the client through whatever it is that we are trying to provide as a service. It is fundamental to the goal of removing all coupling aside from the standardized data formats and the initial bookmark URI.” 4 [emphasis added] So, just by knowing about HTTP and the standard data formats served on the web, you (or a machine) should be able to navigate through a RESTful web service. Moreover, you should be able to create a client application that achieves it’s desired function by navigating through and creating, editing or deleting the resources exposed by the RESTful service URIs.

Suppose we want to create a RESTful ROA web service for banking. We’ll concentrate on allowing clients to make transfers between their various accounts. First, we’ll define the bookmark URI as “/bank”. When a client requests this URI, we’ll respond with something similar to this:

200 OK
Allow: GET
Content-Type: application/xml; charset=utf-8
Location "/bank"

<bank>
 <link rel="/bank/rel/customers" type="application/xml" 
       uri="/bank/customers" />
 <link rel="/bank/rel/branches" type="application/xml" 
       uri="/bank/locations" />
 <link rel="self" type="application/xml" 
       uri="/bank" />
</bank>

The response tells us this URI only accepts GET and then gives us a few options for our next request. We’ll do a GET to the URI “/bank/customers”. Although we don’t know for sure if “/bank/customers” accepts a GET, we can give it a try since we know GET is a standard HTTP verb. If not, the response should tell us what HTTP verbs are accepted. If the web service is RESTful, a GET to any service URI will be safe.1

200 OK
Allow: GET
Content-Type: application/xml; charset=utf-8
Location: "/bank/customers"

 <customers>
  <customer>
      <id>afmorris</id>
      <link rel="self" type="application/xml" 
            uri="/bank/customers/afmorris" /> 
  </customer>
  <customer>
      <id>dlwelch</id>
      <link rel="self" type="application/xml" 
            uri="/bank/customers/dlwelch" /> 
  </customer>
   ... 
  <customer>
      <id>edmorris</id>
      <link rel="self" type="application/xml" 
            uri="/bank/customers/edmorris" /> 
  </customer>
 <link rel="self" type="application/xml" 
       uri="/bank/customers" />
 <link rel="/bank/rel/branches" type="application/xml" 
       uri="/bank/locations" /> 
 <link rel="up" type="application/xml" 
       uri="/bank" /> 
</customers>

The response to our GET is an XML document with a list of customers and their URIs. It also gives us other options we can try if this isn’t what we’re looking for. Let’s make another GET to “/bank/customers/dlwelch”. It’s a good guess this will retrieve my customer info.

200 OK
Allow: GET, PUT
Content-Type: application/xml; charset=utf-8
Location: "/bank/customers/dlwelch"

<customer>
 <id>dlwelch</id>
 <name>Dedra L. Welch</name>
 <address>100 Beach Street, Gulf Coast, TX 70000</address>
 <phone>(555) 555-0000</phone>
 <link rel="/bank/rel/accounts" type="application/xml" 
       uri="/bank/accounts/dlwelch" />
 <link rel="/bank/rel/transfers" type="application/xml" 
       uri="/bank/transferforms/dlwelch" />
 <link rel="self" type="application/xml" 
       uri="/bank/customers/dlwelch/" />
 <link rel="edit" type="application/xml" 
       uri="/bank/customers/dlwelch/" />
 <link rel="up" type="application/xml" 
       uri="/bank/customers" />
 <link rel="/bank/rel/home" type="application/xml" 
       uri="/bank" />
</customer>

The returned XML document does contain my customer info and links to other resources related to my customer account. Notice this URI will also accept a PUT. This is another standard HTTP verb and, combined with the “edit” link, tells us we can edit this representation and PUT it back to the resource URI to update our customer information. But we want to transfer money from savings to checking. So let’s do a GET to “/bank/transferforms/dlwelch”.

200 OK
Allow: GET, POST
Content-Type: application/xml; charset=utf-8
Location: "/bank/transferforms/dlwelch"

  <transferforms>
    <pendingforms>
      <link rel="self"  type="application/xml"
            uri="/bank/transferforms/dlwelch/pending" />
    </pendingforms>
    <postedforms>
      <form> 
        <id>0001</id>
        <link rel="self"  type="application/xml"
              uri="/bank/transferforms/dlwelch/posted/0001" />
      </form>
      ...
      <form> 
        <id>1233</id>
        <link rel="self"  type="application/xml"
              uri="/bank/transferforms/dlwelch/posted/1233" />
      </form>
      <link rel="self"  type="application/xml"
            uri="/bank/transferforms/dlwelch/posted" />
    </postedforms>
    <link rel="self" type="application/xml" 
          uri="/bank/transferforms/dlwelch" />
    <link rel="up" type="application/xml" 
          uri="/bank/accounts/dlwelch" />
    <link rel="/bank/rel/home" type="application/xml" 
          uri="/bank" />
    <link rel="/bank/rel/create" type="application/xml" 
          uri="/bank/transferforms/dlwelch" />
  </transferforms>

We get back an XML representation of the transfer forms available to my account. This URI accepts a POST as well as a GET. That usually means we can append a new item by POSTing to the URI. And we have a link relationship that says “create” with the same URI as the representation we just received. Odds are, we can POST to this URI to start a new transfer by creating a new transfer form. We know that a POST is neither safe nor idempotent1 but the response is encouraging us to do so. This encouragement is the first we’ve seen of our service trying to “guide the client through whatever it is that we are trying to provide”. Previously, our representation links were giving us locations, media types and relationships of connected resources. But in this response, we also have a link that tells us there is a path we can start following that will actually do something.

So let’s try POSTing to “/bank/transferforms/dlwelch”. But what do we POST? There’s nothing telling us of the data or format the service expects from the POST. We could POST the same representation that we received, as suggested by Richardson and Ruby, or we could POST with an empty message body. The response header should tell us if we make an improper POST and also what it expects from us if we try to POST again. Since POSTs are never safe, we’ll try POSTing without any data.

200 OK
Allow: GET, PUT, DELETE
Content-Type: application/xml; charset=utf-8
Location: "/bank/transferforms/dlwelch/pending/1234"

<pendingform>
 <form>
      <id>1234</id>
      <fromaccount><id /></fromaccount>
      <toaccount><id /></toaccount>
      <amount />
      <status>new</status>
 </form>
 <accounts>
    <savings>
      <account>
        <id>1</id>
        <amount>100.00</amount>
      </account>
    </savings>  
    <checking>
      <account>
        <id>4</id>
        <amount>50.00</amount>
      </account>
      <account>
        <id>6</id>
        <amount>1000.00</amount>
      <account>
    </checking>
 </accounts>
 <link rel="self" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
 <link rel="/bank/rel/delete" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
 <link rel="edit" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
</pendingform>

Our POST has returned a new transfer form, number 1234, with a status of “new”. The representation also has account information so we’ll know which accounts are available for transferring money and some links that describe what we should do next. This time the links are all related to the next possible steps in the transfer process. And the allowed HTTP verbs are as well. We can edit the form and PUT is back to the same URI or we can DELETE the form. Although nothing prevents us from returning to a URI we’ve already visited, no other links are provided and we’re now being actively pushed through the completion of the transfer. Let’s edit the form by adding the accounts and amount for the transfer and PUT it back to “/bank/transferforms/dlwelch/pending/1234″.

200 OK
Allow: GET, PUT, DELETE, POST
Content-Type: application/xml; charset=utf-8
Location: "/bank/transferforms/dlwelch/pending/1234"

<pendingform>
 <form>
    <id>1234</id>
    <fromaccount><id>6</id><fromaccount />
    <toaccount><id>1<id></toaccount>
    <amount>75.00</amount>
    <status>ready</status>
 </form>
 <acccounts>
    <savings>
      <account>
        <id>1</id>
        <amount>100.00</amount>
      </account>
    </savings>  
    <checking>
      <account>
        <id>4</id>
        <amount>50.00</amount>
      </account>
      <account>
        <id>6</id>
        <amount>1000.00</amount>
      <account>
    </checking>
 </accounts>
 <link rel="self" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
 <link rel="/bank/rel/delete" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
 <link rel="edit" type="application/xml" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" />
 <link rel="/bank/rel/post" type="application/xml" 
       uri="/bank/transferforms/dlwelch/pending/1234" /> 
</pendingform>

The transfer form is returned to us with a “ready” status, an additional link and another Allowed HTTP verb. It’s apparent that we can complete the transfer by POSTing it to the URI. This is “late binding of an application alternative”4 meant to nudge us through to the end of the transfer. It’s important to note that at if we had POSTed or PUT a form that could not be processed we should receive an error representation with an HTTP error code like “401 Conflict” and other late bound application alternatives – links – to guide us through correcting or cancelling the transfer. But our POST was accepted and the XML response is a representation of the new posted transfer form number 1234. Since we’ve successfully reached the end of the transfer process, we’re now offered several links from which to choose.

200 OK
Allow: GET
Content-Type: application/xml; charset=utf-8
Location: "bank/transferforms/dlwelch/posted/1234"

<postedform>
 <form>
    <id>1234</i>
    <fromaccount><id>6</id><fromaccount />
    <toaccount><id>1<id></toaccount>
    <amount>75.00</amount>
    <status>complete</status>
 </form>
 <link rel="self" type="application/xml" 
       uri="/bank/transferforms/dlwelch/posted/1234" />
 <link rel="up" type="application/xml" 
       uri="/bank/transferforms/dlwelch" />
 <link rel="/bank/rel/account/1" type="application/xml" 
       uri="/bank/accounts/dlwelch/1" />
 <link rel="/bank/rel/account/2" type="application/xml" 
       uri="/bank/accounts/dlwelch/2" />
 <link rel="/bank/rel/savings"  type="application/xml" 
       uri="/bank/accounts/dlwelch/savings" />
 <link rel="/bank/rel/checking" type="application/xml" 
       uri="/bank/accounts/dlwelch/checking" />
 <link rel="/bank/rel/customer/dlwelch" type="application/xml" 
       uri="/bank/customers/dlwelch" />
 <link rel="/bank/rel/branches" type="application/xml" 
       uri="/bank/locations" />
 <link rel="/bank/rel/home" type="application/xml" 
       uri="/bank" /> 
</postedform>

Using REST and ROA, we’ve been able to complete a typical online transaction just by “shuffling paperwork” – obtaining our desired application state by reading, editing, creating and deleting resource representations. Our URIs never contained any method or action information, they were all URIs delivering representations. The actions worked on each resource were the familiar actions that can be taken on any typical web resource – POST, GET, PUT, DELETE.

We have achieved the third level of Richardson’s Maturity Model.5

  1. Every service URI is to a resource representation. And the service is completely exposed through the resource URIs.
  2. All actions taken upon resources are achieved exclusively through our chosen uniform interface. In this case, HTTP – POST, GET, PUT, DELETE
  3. Application state is driven by the client navigating through late-bound, hypertext controls.

But how does our service stand up against Dr. Fielding’s rules for following the HATEOAS constraint?

  1. Our service, and its implied API, does not depend upon the HTTP protocol’s URI scheme. The URIs all point to “documents” in something similar to a file system structure. This could be mapped to any communication protocol – FTP for example. We could change our URIs to something like “ftp://bank/accounts/dlwelch” or “z:\bank\accounts\dlwelch” without effecting our service API or client apps – aside from changing the communication protocol.
  2. We’re using standard HTTP. No changes have been made to the protocol.
  3. Our service only uses the application/xml media type and the link relationships are obvious and self-describing. No out-of-bound knowledge is required to successfully negotiate a transfer.
  4. The resource names for our service are obviously not fixed. New URIs are created and deleted as the users move through the service. Neither does the service have a fixed hierarchy of resources. The client can easily imply the structure from the links available in the current representation.
  5. The client need never know about the resource types used on the back-end of our service. They only need to edit – using appropriate text data – and put the given xml representations back to the URI. For example, it is highly unlikely that the back-end data store of our system has a structure that’s even remotely similar to the pending transfer form representation.
  6. Once a client GETs the bookmark URI, “/bank”, all other states are arrived upon by navigating through the late-bound links provided in the current representation.

Although “there are probably other rules that I (Dr. Fielding) am forgetting”, we’ve made a solid attempt to adhere to the rules “related to the hypertext constraint that are most often violated within so-called REST APIs” – at least to the best of my understanding as a layman.6

You can follow the progress of adding HATEOAS to my RESTful ROA Jazz Service on my development site. For details on the Jazz Service please visit my previous posts on REST and WCF. In my next post on ROA Web Services, we’ll discuss the Uniform Interface.


About the “rel” Attribute: In this example, we included a rel (relationship) attribute in each of our link elements. rel indicates the relationship between the link’s uri and the current resource. Adjectives like rel=”edit” or rel=”self” are commonly used to describe well-known relationships. But if there’s not a standard relationship value you can use a custom relationship like rel=”/bank/rel/delete” which you can describe in your service API. The best part of using the rel attribute with late binding of links is that you can change the link uri without breaking client applications. The client knows what relationship it’s looking for, i.e. what state it wishes to be in, and should follow whatever uri is presented at the moment. There’s a lot more to discuss about the rel attribute, microformats, content-types etc. But I’ll save that for another article. In the meantime, I encourage you to read more about the rel attribute and it’s place in RESTful web services.


1. Richardson, Leonard and Ruby, Sam. RESTful Web Services. Sebastopol: O’Reilly Media, Inc., 2008. Ebook

2. Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000.

3. Fielding, Roy Thomas. REST APIs must be hypertext-driven. Untangled – Musings of Roy T. Fielding, October 20, 2008

4. “The problem is that just being connected is not enough. Yes, it is important, and I’d love to have five or six different ways of explaining the parts of REST that are intended to help grow the Web. But hypertext as the engine of hypermedia state is also about late binding of application alternatives that guide the client through whatever it is that we are trying to provide as a service. It is fundamental to the goal of removing all coupling aside from the standardized data formats and the initial bookmark URI. My dissertation does not do a good job of explaining that (I had a hard deadline, so an entire chapter on data formats was left unwritten) but it does need to be part of REST when we teach the ideas to others.

I like the book’s emphasis on applying REST in practice and running through real examples. Dissertations are not so good for teaching people how to do something (other than to write more dissertations).”

Posted by Roy T. Fielding at 4:04:29 PM

5. Fowler, Martin. Richardson Maturity Model – steps toward the glory of REST. martinfowler.com, March, 18, 2010

6. Fielding, Roy Thomas. Specialization. Untangled – Musings of Roy T. Fielding, October 24, 2008

Tagged with: ,
Posted in REST, Web Services
Advertisement