Home > REST > Building RESTful services – part 2

Building RESTful services – part 2

This is part 2 of “Building RESTful services”.  In Part 1 we talked about Level 1 and Level 2 of the Richardson Maturity Model.

Level 3 – Hypermedia

Hypermedia is really the most interesting aspect of REST and the aspect that is key to scalability and loose coupling. Sadly this is also the aspect that most services that aim to be RESTful have trouble with.

As humans using a web browser, we use this concept every single day. If you wanted to look at the sports section of msn, what do you do ? You type in http://www.msn.com in the browser, look for the hyperlink with the title “sports” and click on it. This concept of hyperlink is so simple, but is one of the most important reasons for the popularity and scalability of the web. Instead of using a hyperlink, imagine if you had to download a “document or a manual” for msn.com that explained the URI to type in to the browser for each section and every time you needed to access something you had to look at the document, find the URI corresponding to your section and type it into the browser. Now imagine if you had to do this for every single website, each having a different kind of document in a different custom format. The internet would certainly not be what it is today. Yet a majority of the APIs built today make us do this. This is where the hypermedia concept comes in.

Let’s say there is a bookstore which has an api for clients to access various lists (e.g. top sellers, new arrivals etc). To get a list of top sellers in the java category, a client may make the following call

GET /estore/lists/topsellers/software/java?count=5

and this returns the following


<books count="5">
 <book purchased="true">
     <id> 12345 </id>
     <title> Effective Java </title>
     <author> Joshua Bloch </author>
 </book>
 <book>
     <id> 23456 </id>
     <title> Java Concurrency </title>
     <author> Brian Goetz </author>
 </book>
...
...
...
 <book>
     <id> 56789 </id>
     <title> Clean Code</title>
     <author> Bob Martin </author>
 </book>
</books>

This would typically be accompanied by documentation such as

Method – Get More Details about a book
Call the following URL
/estore/books/id/

Method – Get Reviews for the book
Call the following URL
/estore/books/id//reviews

Method – Provide a “recommended” rating for the book
Call the following URL. Note that only customers who have purchased the book can provide the recommended rating (purchased flag should be true)
/estore/books/id//rating?level=&customer=

Method – Get Next set of books in the list
Call the following URL
/estore/lists/topsellers/?startIndex=&count=

It is the client’s responsibility to make sure that startIndex and count have the right values.

We’ve all seen API’s built and documented this way. This approach puts the burden of generating the URIs for subsequent actions completely on the client. These are the potential issues with the above approach -

  • Errors in constructing the URIs

The primary responsibility for constructing the URIs rests on the clients, for any action that the client wants to perform based on the list. This is a simple use case, but as things get more complicated, the likelihood of a client constructing the URI incorrectly increases a lot.

  • Tight Coupling

Since the URI formats need to be published and specified ahead of time for the clients to construct them, the service has very little flexibility in terms of changing the API. For example if the service wants to point to a completely different URL for getting reviews,  it would be tough to do so without breaking existing clients.

  • State transitions and business rules on the client side

Let’s say the service changes the business rule and any customer can now provide a rating for the book. This means that all clients that are using the current API now need to change their code to make this happen. This tightly couples business logic to client side code and severely restricts the service’s ability to iterate over business rule changes without massive coordination efforts with all clients.

This is where hypermedia shines. Take a look at the response below with additional hypermedia links (Relative links are used for brevity, but assume all the links are full links including host name)

<books>
 <book purchased="true">
     <id> 12345 </id>
     <title> Effective Java </title>
     <author> Joshua Bloch </author>
     <link rel="moreInfo" href="/estore/catalog/books/id/12345"/>
     <link rel="reviews" href="http:/reviewStore/estore/reviews/id/9912345&customer=888"/>
     <link rel="rating" href="/estore/books/id/12345/rating?customer=888"/>
 </book>
 <book>
     <id> 23456 </id>
     <title> Java Concurrency </title>
     <author> Brian Goetz </author>
     <link rel="moreInfo" href="/estore/catalog/books/id/23456" />
     <link rel="reviews" href="http:/reviewStore/estore/reviews/id/9923456&customer=888" />
 </book>
...
...
...

 <link rel="next" href="/estore/reports/topsellers/software/java?startIndex=6&count=5"/>
</books>

So what’s different here ? We have three new elements which contain a hyperlink with some metadata. Let’s look at each of them

    <link rel="moreInfo"> ... </link>
 Instead of trying to construct the link for more details, the client can simply look at the response for the “moreinfo” tag and if it exists, it knows that there is additional info available. Pretty simple.
    <link rel="reviews"> ... </link>
  Once again, instead of trying to construct the URI, the client looks for the presence of “reviews” link type. Notice that the link to the reviews can now be changed by the service if necessary. Maybe the site decides to consolidate the reviews for all their product line and also decides to track which customers look at reviews and so on. Since the URI is fully generated by the service and the client merely looks for the appropriate hyperlink, not only do the clients not break, they don’t even have to realize that the underlying hyperlink changed.
    <link rel="rating"> ... </link>

If you recall, the initial business rule was that only if the purchased=”true” flag is available, the client should display the rating link to the customer, and a change in this business rule affected the client. Instead, here notice that based on the business logic, the Service can either send back, or not send back a link called “rateBook”. The client simply looks for this link, and if it is not available, doesn’t display the rating link.

Similar benefits are realized by the presence of a “next” link. These  examples highlight the point that by adding hyperlinks to control next steps and NOT making the clients construct URIs, the system as a whole has gained a lot of flexibility.

HATEOAS

    Hypermedia As The Engine Of Application State or HATEOAS refers to allowing clients to navigate through appropriate application states using hyperlinks. The above examples touch on this a little, but let’s look at this from a slightly different point of view. Going back to our eStore example from PART 1, let’s start with an empty order

GET /estore/order/178567

<order>
    <cart>
       <items>
       </items>
       <link rel="update" href="/estore/order/178567/cart"/>
    </cart>
</order>
Note that the link with the type “update”  indicates that the only possible next step for this order is to update the cart by adding more items. The absence of links such as “payment”  or “cancel” imply that those are not valid steps at this point. Let’s say a couple of items get added to the cart by making calls such as
POST /estore/order/178567/cart
<item code="abc" qty="1"/>

//another item
POST /estore/order/178567/cart
<item code="def" qty="1"/>

 Now our order looks something like this

GET /estore/order/178567

<order>
    <cart>
        <items>
            <item code="abc" qty="1">
            <item code="def" qty="1">
        </items>
        <link rel="update" href="/estore/order/178567/cart"/>
    </cart>
    <link rel="saveForLater" href="/estore/customer/1356/archive/order/178567"/>
    <link rel="cancel" href="/estore/order/178567"/>
    <link rel="address" href="/estore/order/178567/address" type="application/vnd.estore_address+xml"/>
</order>

Notice that there are several new links as part of the order itself which indicate one of several next steps that can be done as part of the shopping process.

  •  type=saveForLater
    • The presence of the link of the type “saveForLater” indicates that one valid option for this order is for it to be saved for later, so that the customer can come back to it at some point. Note that if there were no items in the cart, this link would not be available .
  • type=cancel
    • Indicates that this order is in a state where it can be cancelled if the customer wants to, (by making a delete request – this does need to be documented as part of a simple resource documentation). Again, only available for a non empty cart
  • type=address
    • This serves as the link to enter the address for the order so that the checkout process can start.

The important point here is that the client doesn’t have to figure out from an external document as to what are the possible next states for the order. The possible next states are all captured with hyperlinks. Again, this makes for really loose coupling and provides the service a lot of flexibility in evolving business rules around Order processing without breaking existing clients. Also note that each of the link types either returns or accepts a certain content-type. While it is tempting to use application/xml, a specific format such as application/vnd.estore_order+xml, or application/vnd.estore_address+xml is preferred. Another important thing to keep in mind is that while the client is expected to use the hyperlinks from the response, the service should still guard against invalid requests for a given state.

Let’s go one more step, and assume that the customer decided to enter the address for the order.

POST /estore/order/178567/address
<address>
    <street> ... </street>
    <zip> ... </zip>
     etc etc
</address>
Now that the address for the order has been entered, let’s see what can be done with the order now.
GET /estore/order/178567
<order>
    <link rel="cart" href="/estore/order/178567/cart"/>
    <link rel="saveForLater" href="estore/customer/1356/archive/order/178567"/>
    <link rel="cancel" href="/estore/order/178567"/>
    <link rel="address" href="/estore/order/178567/address"/>
    <link rel="payment" href="/estore/order/178567/payment"/>
</order>
  • Payment
    • The presence of the link of the type “payment”  indicates that payment is a valid next step for the order in its current state. If the cart was empty with no items, or if there was no address associated with an order, our made up business rules wouldn’t have allowed us to enter payment information. But now, based on the link, the customer can proceed to payment
As you can see, the set of possible next steps are provided by the service at any given point using hyperlinks. This is HATEOAS. Now assuming the payment information was provided and successfully validated, the order now goes into a state where the only possibility is to cancel the order ( since our made up business rules allow for an order to be cancelled till it ships)
GET /estore/order/178567
<order>
    <link rel="cart" href="/estore/order/178567/cart"/>
    <link rel="cancel" href="/estore/order/178567"/>
</order>

Allowing clients to dynamically discover  next logical steps using hyperlinks, instead of upfront specification is a key part of RESTful design. Thinking in this way helps us realize a lot of benefits of  RESTful services. Finally, taking this point further, it makes a lot of sense to expose only the stable starting point for a service, and allow clients to discover capabilities on the fly. For example, our eStore could simply advertise the  well-known URI /estore/ and everything else is simply discovered by the client by following relevant links provided by the service at the right time.

GET /estore/
<estore>
   // Get current specials
   <link rel="specials" href="/estore/specials"/>
   // Starting point for all catalog related info
   <link rel="catalog" href="/estore/catalog"/>
   // POST to start a new order
   <link rel="order" href="/estore/order"/>
   // Starting point for all customer specific interaction including login
   <link rel="profile" href="/estore/customer"/>
</estore>

Conclusion

While the concept of hypermedia is simple, it is the one aspect that people building RESTful services ignore most often. Using hypermedia is really significant in making a web service loosely coupled and highly flexible, allowing it to evolve over time without breaking clients.

References

  1. http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
  2. http://www.crummy.com/writing/speaking/2008-QCon/act3.html
  3. http://en.wikipedia.org/wiki/Representational_state_transfer
  4. http://www.infoq.com/articles/rest-introduction
  5. http://restinpractice.com/default.aspx
About these ads
Categories: REST
  1. April 7, 2012 at 1:50 pm

    Good summation of REST concepts.

    Regarding the comment about HATEOAS you made mentioning that the client doesn’t have to look at external documents to determine possible next states of the order, how would you implement the navigation of hypermedia links in a client?

    You could present different interfaces to a user from the client of the RESTful API based on the links presented, however some prior knowledge of possible states must be needed to provide sensible interfaces to the user? Doesn’t this mean that, in practice, there’s still a reasonable amount of coupling between the client and RESTful API? If you added a step to your order placement API requiring separate shipping and billing addresses, for example, you’d need to update the clients to be able to deal with that added step as, although they’d be presented with the link to the new resource, they wouldn’t know what to present to the user for that resource.

    • April 14, 2012 at 8:16 pm

      Nick, I think there are two slightly different things in play here. First is how the client even knows what the possible states are and what they mean. You are right about this, that it does need to be documented in some form. However this can be at a reasonably high level of abstraction. E.g. there exists the step “shipping address entry” and this step corresponds to entering the full shipping address for the order. The second thing, is how the client finds out about this state. REST simply says that this should be discovered via hyperlinks (e.g. on seeing rel=”shippingAddressEntry” in the response), as opposed to describing the rules externally ( “if there is at least one item in the cart and the customer has entered payment information, only then should the customer be allowed to enter the shipping address”). So the states themselves couple the client to the RESTful API, but the logic as to when a certain state becomes valid, or the logic of determining which is the next state(from a set of known states) can be decoupled from the client. If the service adds an entirely new state, then yes certainly clients would need to be aware of what to do with the state and be changed to look for the state and act on it. But they still don’t have to contain the logic of when that state becomes active or what are the state transition rules that cause a workflow to go into that state. Hope this clarifies my points.

  2. Goran
    April 8, 2012 at 2:06 pm

    Nice work… really professionally done article

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: