Using Hypermedia To Design Event-Driven UIs

Some of the most powerful tech is now built fully on an API backbone. Web APIs are great because they can deliver data and functionality over HTTP — or other protocols — meaning agnostic client platforms, easier evolvability, and more efficient designs. Considering API design, though incumbents like GraphQL or gRPC are picking up attention, REST, as defined by Roy Fielding, is still a champion style.

In our effort to find stunning REST API best practices, we’ve covered what it means to design a true REST machine, (and clarified those points). APIs also deserve quality change management to be effective and evolvable.

We’ve seen that although RESTful APIs transfer data and function well across a variety of client devices, truly REST APIs that adopt hypermedia can ignite interconnected, event-driven flows. Hypermedia can enable robust, evolvable applications that reduce client weight. But how does hypermedia work in practice? Most importantly, how would a hypermedia-native web application actually function?

Again, we turn to Nordic APIs veteran speaker and author Asbjørn Ulsberg for an answer. Asbjørn suggests API developers consider a combination of compositional UIs with hosted views and hypermedia for building reactive, event-driven web applications.

This article traces an excellent talk given by Asbjørn Ulsberg at the Nordic APIs 2018 Platform Summit:

Hypermedia, Hosted Views, and Compositional UIs

First, let’s define these three terms, as they will become important as we describe the makeup of a hypermedia-native user interface.

1. Hypermedia

Hypermedia is the final — and diehards would argue most important — REST constraint. As we’ve discussed before, hypermedia places a greater operational emphasis on resources with affordances, located at URIs. Hypermedia APIs are conditional; reactive to when and who is giving the request. Therefore, you can think of hypermedia as a menu and a recipe – a way to describe which options you have and how to perform them

As you drive through a city, you don’t follow an old static road map — you rely on traffic lights, road signs, construction detours, and other obstacles in your direct path. Similarly, hypermedia works at runtime and is conditional to the nature of the request.

“This runtime conditionality of hypermedia is one of its great strengths”

2. Hosted Views

Hosted views are essentially web components connected to a server-side API, often a microservice. Specifically, hosted views are implemented using an <iframe> and JavaScript through a server-side API. Hosted views are commonly implemented using this method, however, Web Components is a growing standard that could replace this method once it matures.

3. Compositional UIs

Composition: The act of combining parts or elements to form a whole

Compositional UIs could be entire pages, iframes, popups, or other window objects. Asbjørn recognizes that these components work independently, and rely on a standard communication protocol. In JavaScript, this is done using window.postMessage(), along with callbacks that every component can subscribe to.

Sample Hypermedia Implementation: Online Store

So, how are these three aspects pieced together in practice? Asbjørn suggests we consider how an online store operates. Online shops don’t want to handle payment processing on their own — it’s a great technical burden, and such sensitive data is also tricky to navigate in the wake of GDPR.

For a user to check out an online shopping cart, for example, requires both user and payment data. Thankfully, such data can be collected and inserted using hosted views, which outsource the burden to others.

In order to host a view that can collect user data, Asbjørn describes how an API request may look. First, we make a POST call to create a login resource:

POST /logins HTTP/1.1
Authorization: Bearer f489l3…
Host: api.example.com

The response lists some operations that may provide alternative clues for logging in:

HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.example.com/logins/38237dfyw78

{
	"id": "https://api.example.com/logins/38237dfyw78",
	"operations": [{
		"rel": "login",
		"method": "GET",
		"href": "https://api.example.com/logins/38237dfyw78.js",
		"contentType": "application/javascript"
	}]
}

You’ll notice that the login operation above also included an href property pointing to a URI. The next step is taking that URI and embedding it into the webpage.

Here’s a snippet of inserting the URI within a script tag, with the data attribute pointing to the div we want the view hosted in.


This will result in the creation of a login view in an eCommerce site. The beauty of this is that it was developed hypermedia, as in Hypermedia as the Engine of Application State (HATEOAS).

Now, when we login this occurs within the hosted view:

POST /logins/38237dfyw78/identifications HTTP/1.1
Authorization: Bearer f489l3…
Host: api.example.com
Content-Type: application/json

{
	"identify": {
		"email": "text@example.com",
		"phone": "5555555555"
	}
}

The hosted view thus collects the data from the HTML form and submits it as an API request to its backend API. If the user is identified, we will recieve a 302 Found response. The makeup of the identification response may vary, especially depending on if you’re using an identity management suite with OAuth and OpenID Connect.

Regardless, the 302 Found response is now translated into a JavaScript event callback named consumer-identified. The shop will store the URI with the identified consumer for later use. A 302 Found identification response will also likely detail an operations property, containing a parameter that suggests a subsequent operation to perform. For example, such an operation may be included within an identification response:

...
"operations": [{
	"rel": "next acquire-shipping-address",
	"method": "POST",
	"href": https://api.example.com/consumers/38237dfyw78/addresses
	"expects": {
		"address": "string",
		"city": "string",
}
...

Above, we see the next operation is to collect a shipping address, including what data to accept and what type; in this case, both are strings. There may be multiple potential future operations to perform, so using link relations with next allows us to distinguish them, and helps inform the hosted view which operations to perform next.

In turn, an HTML form would look like this:


This form can then render a view within the container. Asbjørn notes that everything we need to perform in this request was available in the previous response, meaning that the frontend is completely stateless.

“The frontend has no logic on how to build the URI; the URI is a completely opaque identifier and nothing more”

Asbjørn sees a few benefits in not forcing clients to build URIs. It takes the burden off the client. It means that the API doesn’t have to expose internal identifiers to the external world; it just uses opaque URIs as the identifiers; decoupling the client from the server.

To finish the shopping implementation, we would continue to grab URIs within operations of the API responses, and add them to HTML documents to then affect the end user interface. Now, when someone fills out the form, a shipping address request is made against the backend API based on the operation found in the initial identification response. We then would receive a response from the shipping address request, which could be turned into an address-submitted event. A similar process can be used to initiate payments and display payment-received pages using hosted views.

Takeaways

Once events in the front end communicate using a message protocol, such components are decoupled from one another, making them reusable and enabling them to change independently. In general, it’s a great idea to detail potential further actions as hypermedia links within your API responses. As Asbjørn notes, this will enable Role-Based Access Control (RBAC), and other state variations.

Using hypermedia found in responses from such backend APIs, compositional UIs become reactive and event-driven. In doing so, business logic and complexity are removed from the client interfaces and placed in the API. Since this method does place more emphasis on URIs, Asbjørn reminds us to not expose internal identifiers to the client: Client-side URI construction causes coupling.

Building An Event-driven, Hypermedia Architecture

In this article, we’ve seen how hypermedia can be used in practice to build interconnected, compositional UIs. By basing subsequent actions on operations nested within API responses, hypermedia enables developers to create smart, stateless experiences all based on the ubiquitous URI.

Self-documenting processes are common in the physical world, and they can behave well in the digital realm as well. As Asbjørn describes, hypermedia is akin to:

“…a self-documenting process giving you clues as to what you can perform at each stage”

For some, HATEAOS adoption may be too much of a hassle, and may not be relevant for all scenarios. However, PayEx is building their Checkout using this exact architecture, so it appears to work in practice. Hypermedia formats such as Siren, Hydra, andJSON Hyper-Schema can also help ease adoption.

Resources