Hypermedia APIs with mobile clients

At our recent talk at Confess 2013 in Vienna there were several developers working with mobile devices in the audience. This is great as the purpose of hypermedia in the API is to make life simpler for client developers. However, after the talk several of them raised issues regarding the high latency and low bandwidth of mobile phones such as “All these links requires a lot of back and forth requests between the client and server” and “The links will add unnecessary data which will make the documents bigger”. In this post I will try to address both these concerns.

Hypermedia and low bandwidth

Lets start with the problem of low bandwidth. When adding hypermedia to your API the documents will certainly grow in size. However, will it become large enough to be a real problem?

If adding a single link (say 50 bytes for a non-trivial link) is an issue for you then you probably should not use HTTP either as this brings a lot of extra overhead in the first place. The concern that the client developers raised was when using collections where each item contained one or more links. In this case it could quickly become a problem. For example this collection of books:

The links definitely takes up a lot of space. If only we could represent each URI once and then reuse it for each item…. We can! There is an RFC exactly for this purpose: RFC 6570 – URI Template co-authored by none other than Roy Fielding. This probably means that we are “allowed” to use this standard and still be RESTful! :-)

Rewriting the links the JSON could look like this:

Nice!

Hypermedia and high latency

But there still is a problem. To display the books the client must fetch each item link to get the authors, image and other important information. This is definitely something we need to address! The mediatype application/hal solves this by using something called embedded resources. These can include all or parts of the content of one or more sub resources. In this example I combine embedded resources and URI templates. I have only added authors, but of course more data could be added as necessary. I also show a way to override links:

I hope this example convinced you that a hypermedia API does not imply lots of requests and unnecessary bloat. Instead I think it is the complete opposite. In my view, to create a useful API the server developers must understand the needs of the clients and their usecases. By using hypermedia this becomes even more clear, as the server developers are responsible for providing the next actions a client can take from each point in the application. If an API is bloated and requires lots of unnecessary requests this is not because of hypermedia. Instead it is probably ignorance from the server developers. They might be unaware of the needs of mobile clients or even the fact that they have mobile clients!

Do you have any other concerns about using a hypermedia API? Please let me know!

This Post Has 14 Comments

  1. “…server developers are responsible for providing the next actions a client can take from each point in the application.”

    This is where HATEOAS (Hypermedia as the Engine of Application State, http://en.wikipedia.org/wiki/HATEOAS) comes into play. Spring has already a nice supporting library (spring-hateoas, https://github.com/SpringSource/spring-hateoas).

    More on this: http://weblogs.java.net/blog/mkarg/archive/2010/02/14/what-hateoas-actually-means

    Cheers,
    Mariusz

  2. Nice article. It is also possible to use compression to lower bandwidth usage.

    http://en.wikipedia.org/wiki/HTTP_compression

    For example in Apache:
    http://httpd.apache.org/docs/2.2/mod/mod_deflate.html

    As long as there is bandwidth available we can embrace hyperlinks. :)
    http://ajaxpatterns.org/RESTful_Service#Embrace_hyperlinks

    We are, in a product, also using “detail levels” on resources when requesting them. That means that a book can be differently populated when requested by id (full detail) or embedded as your books are above (medium detail) in a “book shelf list” for example. But when requesting the “book top list” they can have higher detail, depending on how much information a normal client usually displays, to be able to keep the number of http calls to a reasonable amount, to keep a good balance between a coarse-grained and fine-grained service architecture.

    http://www.ibm.com/developerworks/webservices/library/ws-soa-granularity/

      1. Compression is good, but we should also consider battery usage when talking about mobile clients. It could be bettet to download a few extra bytes instead of spending cpu cycled on decompressing.

        1. Agree. It really makes sense!

    1. Have you seen any studies comparing CPU cycles vs transmitting and receiving data? My intuition says that at least transmitting 3G data drains the battery more than a few CPU cycles…

      The good thing is that this can be controlled by the Accept-Encoding header so the client can actually make this decision.

  3. You’re probably right, I need to find some numbers on CPU while transmitting, but I’ve always been hearing that bringing the radio chip up from sleep is crazy expensive compared to just keeping it alive a little longer.

    true – client choice rules!

  4. One issue with hypermedia that’s been burning for a while is with single page applications (SPA) and the browser history. With an SPA you’ll need to keep an in-memory history of all the previous API states that you’ve visited so that if/when a user hits the browser’s back button, your SPA would know what data, rels or links are available. I haven’t seen a good solution to this.

    An even larger issue is when the user refreshes the page on a SPA, you end up loosing all those previous states that you were in. How do you, at that point, figure out which relation/url you need to hit in order to get the data that you need to re-render that page? You can’t even start from the root again ’cause the browser lost all context of where you came from.

    Any comments to this would be welcome.

    1. Hi Andrew!

      It may be that we view SPAs differently, or Hypermedia clients, but I’m interested to hear more about what you mean regarding SPAs having to keep an in-memory history of all previous API states. Maybe there are some aspects of Hypermedia I’m missing out on.

      The way I see it is that SPAs can be designed to consume any type of API, be it Hypermedia or [non-Hypermedia non-HATEOAS “REST”-ish APIs] or something else. I think you would agree.

      I’d like to describe how I did my last SPA, and then I’m interested to hear from you how you think it may apply to your concern, if at all, or if your concern is of a different kind.

      The last SPA I did was with AngularJS, and I consumed an HTTP API with JSON payloads, which had HAL links in them, with a rel on each link. The entry point was the only one I knew the URL for, and from there I had to follow links based on rel to each other resource. Depending on how you interpret the term Hypermedia API, this may have been one.

      In AngularJS they have support for routing and HTML5 History API. Here’s a link to the “routing” part of the AngularJS tutorial for overview: http://docs.angularjs.org/tutorial/step_07 . I used this to give each state in the SPA a specific URL, which I had routed in my SPA to show a specific view, and any parameters in the URL were used to keep track of any state that was needed in that view. My goal was that the user should be able to reload their browser at any time, and see the same view they saw before reload.

      For example the browser URL https://SPASERVER/myaccount would be routed to the MyAccount view I programmed in AngularJS, which would use AngularJS’ data-binding to show data from inside the CurrentUser Resource Service (see below). The browser URL https://SPASERVER/items/details/https%3A%2F%2FAPISERVER%2Fapi%2Fitem%2Ffull%2F123.json would route to the ItemDetails view, and it would pick up the URL part after “details/” and use that link to fetch the details it needed from the API server. Note that I didn’t put just the id of the item there when I created the clickable link for the user, but the entire API link, because that’s the identifier the server gave me about the item. I used encodeURIComponent() to put it in the clickable link URL correctly, and then decodeURIComponent() when I read it in the ItemDetails view.

      In AngularJS you can also create lots of Services of your own, and have them injected into other Services or Controllers, which can connect those Services or their data to Views. Each Service instance is usually a singleton.

      What I did was to set up a Resource Service I programmed for each known type of resource I knew would exist on the server. For example MyBackupJobs was such a Resource Service. That Resource Service instance would know that it needed to find its URL in the link with rel “backupjobs”, in the CurrentUser Resource Service. It in turn needed to find its URL in the EntryPoint Resource Service, in the link with rel “currentuser”. The EntryPoint was of course the only one which knew its URL on beforehand.

      Having the required Services injected into each other, they could use the AngularJS feature $watch() to see when their upstream Resource Service changed its loaded data (whenever it was loaded or emptied), and find their respective link in there. Or find that the link disappeared if the server removed it.

      My Resource Services would also expose the status of their finding their URL in a link, or finding it removed from a dependency Resource Service. (If their link disappeared, they would also empty their previously loaded and cached data.) They would also expose whatever data was loaded from their link. The data would be what contained other links in the HAL specified “_links” key.

      This all meant that I could connect the Resource Services’ availability and data, out into other parts of the app, including the views, and have the app react to whatever data and links changed from the server.

      It also meant that if the user reloaded their browser at any time, the URL in the browser would be routed to the correct view, and the connection to the Resource Services, and their connections to each other, would mean that they would download the resources and links needed, starting from the entry point, to find the data needed for that view. Then when the user continued clicking around, a lot of that stuff would be cached in the Resource Services.

      What do you think, Andrew? Does this relate to your issue at all? I’m very interested in hearing your take on what Hypermedia APIs are and how they should be consumed, because I’m still trying to figure out the best ways to build and consume Hypermedia APIs, in a practical way.

      Thanks,
      Hugo

      1. Hugo,

        That is probably the best answer I’ve heard on this subject. This makes perfect sense. The missing piece for me was that I didn’t even think about storing rel URI’s into the browser’s URL. That pretty much solves 85% of the problems I was having. You got rid of the necessity of keeping track of where the user has been by putting the URIs into the browser history.

        You really should’ve made that reply as its own blog post. It was amazing.

        Thanks much. Now, I’m going to go read it again.

      2. I know that this is an old link, but with such a great response, I had to say something.

        Personally I disagree with putting the api link in the browsers url as it makes it ‘unfriendly’. That being said I haven’t really come across an approach that works. Currently my api is generating all the links, but my client side is still constructing them in some cases. My biggest issue is dealing with browser refreshes or deep link navigation (link is copied and pasted).

        With the client side service that you described, how does it deal with non-direct links. As an example, user does a search (ie /items/$filter=abc) that returns 5 results. They then follow one of the links to /items/2. If the user refreshes the browser, how can you follow server links to arrive back at /items/2? Do you have an example of your service posted anywhere, I’d love to take a look at it. Maybe it would give me a little more clarity on what exactly it is doing.

    2. Make sure that you modify the URI in the browser as your application changes state. This URI should represent the current state of your application, and not the path you have navigated. This solves the issue of sending links to a friend or reloading the page.

      You should also have a look at this approach to building your client. This way your SPA could be loaded directly from a specific document.

      http://blog.jayway.com/2012/08/01/combining-html-hypermedia-apis-and-adaptive-web-design/

Leave a Reply

Close Menu