Enterprise Java

RESTful services with HATEOAS. Hypermedia: The Secret Ingredient of REST

In this post, we feature a comprehensive article about RESTful services with HATEOAS. Hypermedia which is the Secret Ingredient of REST.

1. Introduction

In the previous part of the tutorial, we took some time to refresh our knowledge regarding the foundational principles of the REST architectural style. The critical look at the state of the REST as interpreted by the industry unveiled the disappointing truth that one of its key constraints, hypermedia as the engine of application state (HATEOAS), is very often omitted altogether.

Hypermedia is defined by the presence of application control information embedded within, or as a layer above, the presentation of information. Distributed hypermedia allows the presentation and control information to be stored at remote locations.

https://www.ics.uci.edu/~fielding/pubs/dissertation/web_arch_domain.htm#sec_4_1_3

The theme of this part is hypermedia, and particularly HATEOAS. Hopefully, not only we are going to be convinced how important it is, but also underpin a number of strategies to enrich our REST web services and APIs with hypermedia capabilities.

2. What all this “noise” is about?  

As we already know, being stateless is one of the mandatory constraints of the REST architectural style. From the other side, the overwhelming majority of the real-world web services and APIs have to deal with state management. Does it look like REST ignores the realities and needs of modern software systems?

Absolutely not, REST architectural style acknowledges the importance of state management and suggests the solution to it in the form of hypermedia, the engine of the application state. On the server side, the usage of the hypermedia advertises not only the relations between resources but also the actions which could be potentially applied to the resource. On the client side, the presence of the hypermedia brings the discoverability aspect in terms of the next actions, steps or state transitions to be taken. Ideally, the client has to know just one single URI entrypoint, everything else comes from the server through hypermedia.

Obviously, the client has to be smart enough to be able to browse through hypermedia controls in actionable fashion. As many experienced web service and API developers have noticed, the hypermedia support on the server is not that hard but on the client is substantially more difficult.

But what exactly is hypermedia in the context of web services and APIs? We can think of it as an additional meta-information which server sends to the client along with response. It primarily consists of accompanying links to related resources and, most importantly, contextual actions applicable to the resource in question to alter its state.

When I say Hypertext, I mean the simultaneous presentation of information and controls such that the information becomes the affordance through which the user obtains choices and selects actions. Hypermedia is just an expansion on what text means to include temporal anchors within a media stream; most researchers have dropped the distinction.

https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-718

All together, these pieces of metadata tell the client where to fetch more data or what kinds of operation are valid in the current context. If it sounds exciting to you, it really is.

So we talked a bit about what hypermedia is and its important place in the REST architectural style, it is time for us talk about the “how” of it.

3. HATEOAS in the Field

Just to reiterate over what we have said in the previous part, REST architectural style mandates the use of the hypermedia but does not specify how to do it. Obviously this loophole had to be closed and over the years it led to proliferation of different styles and specifications. Many companies went even further and came up with own hypermedia types, blueprints and recommendations.

Hypermedia Types are MIME media types that contain native hyper-linking semantics that induce application flow. For example, HTML is a hypermedia type; XML is not.

http://amundsen.com/hypermedia/

Surprisingly, up to this moment there is no single generally accepted standard to follow in order to empower web services and APIs with hypermedia elements. A few are more or less widely adopted whereas others are occupying a pretty narrow niche. Moreover, most of the specifications are still evolving and are advertised as work in progress.

Why there are so many specifications and what are the key differences? By and large, the evil comes from the details, more specifically in describing related resources, links and actions / operations. How to find the best fit for you? Frankly speaking, the choice of the suitable specification directly impacts the amount of efforts required to support it. In case of a green field web service or API, you pretty much have a freedom to pick and choose any. By contrast, when you are maintaining an existing web service or API and tasked to enrich it with hypermedia support, the choices are suddenly very limited (unless you are given the luxury of full rewrite).

From the implementation perspective, the majority of the specifications gear towards JSON format as representation of the resource state and hypermedia controls. However, there are outliers which exploit the HTTP protocol headers instead. This is certainly not dictated by REST architectural style but comes from the reality of modern web services and APIs, they all are HTTP oriented.

Thus, what are these specifications?

3.1. RFC 5988 (web linking)

We are going to start with RFC-5988: Web Linking. It specifies relation types for Web links, defines a registry for them and also defines the use of such links in HTTP headers with special Link header. Here is the quick example:

Link: <https://rentals.jcg.com/reservations>; rel="self"; title="reservations"

For sure, there could be more than one link encoded into the Link header (or multiple Link headers), for example:

Link: <https://rentals.jcg.com/reservations?page=1>; rel="previous"; title="previous page", 
<https://rentals.jcg.com/reservations?page=3>; rel="next"; title="next page"

Web Linking is the most basic and easiest way to introduce hypermedia support. New or existing APIs, it is quite straightforward to bake it in, however from the capabilities perspective, web Linking offers very limited set of options, primarily simple relations and no support for actions and collections.

3.2. HAL

JSON Hypermedia API Language, or HAL, establishes the conventions for expressing hypermedia controls (links and resources) using JSON. It was created by Mike Kelly back in 2011. The web services and APIs emit HAL documents so the clients could extract the appropriate links and, depending on their relation type, navigate through them.

Although HAL specification is still in a draft state, its design principles gained it the place to become one of the top choices for modern web services and APIs driven by hypermedia.

The primary design goals of HAL are generality and simplicity. HAL can be applied to many different domains and imposes the minimal amount of structure necessary to cover the key requirements of a hypermedia API.

https://tools.ietf.org/id/draft-kelly-json-hal-02.html

As you may guess, the resource representations are HAL documents in the JSON format which use dedicated media type application/hal+json.

{
  "_embedded": {
    "reservations": [ {
      "id": "ce5886acbb87",
      "vehicle": "Volkswagen Golf 1.2 TSI",
      "from": "2020-02-01",
      "to": "2020-02-12",
      "_links": {
        "self": {
          "href": "https://rentals.jcg.com/reservations/ce5886acbb87"
        },
        "customer": {
          "href": "https://rentals.jcg.com/customers/fed195a03e9d"
        }
      }
    }, {
      "id": "fc14e8ef90f5",
      "vehicle": "BMW 325i",
      "from": "2020-01-10",
      "to": "2020-01-12",
      "_links": {
        "self": {
          "href": "https://rentals.jcg.com/reservations/fc14e8ef90f5"
        },
        "customer": {
          "href": "https://rentals.jcg.com/customers/fed195a03e9d"
        }
      }
    } ]
  },
  "_links": {
    "first": {
      "href": "https://rentals.jcg.com/reservations?page=0&size=10"
    },
    "self": {
      "href": "https://rentals.jcg.com/reservations?page=0&size=10"
    },
    "next": {
      "href": "https://rentals.jcg.com/reservations?page=1&size=10"
    },
    "last": {
      "href": "https://rentals.jcg.com/reservations?page=1&size=10"
    }
  },
  "page": {
    "size": 10,
    "totalElements": 13,
    "totalPages": 2,
    "number": 0
  }
}

In addition to that, HAL supports URI templating and documentation for link relations. Unfortunately, HAL does not provide any support for actions (another name you may have heard is affordances). One of the derivative specifications which tries to fill this blank spot in HAL is HAL-FORMS. Essentially, it is just an extension of HAL which augments it with the ability to express methods as well as properties.

{                                                                       
  "id": "13e1892765c5",                                                
  "vehicle": "Honda Civic 2020",                                       
  "from": "2020-01-01",                                                
  "to": "2020-01-05",                                                  
  "_links": {                                                          
    "customer": {                                                      
      "href": "https://rentals.jcg.com/customers/fed195a03e9d"           
    },                                                                  
    "self": {                                                          
      "href": "https://rentals.jcg.com/reservations/13e1892765c5"        
    }                                                                   
  },                                                                    
  "_templates": {                                                      
    "default": {                                                       
      "method": "put",                                                 
      "properties": [ {                                                
        "name": "from",
        "regex" : "yyyy-MM-dd",
        "required": true
      }, {                                                              
        "name": "to",
        "regex" : "yyyy-MM-dd",
        "required": true
      }, {                                                              
        "name": "vehicle"                                              
      } ]                                                               
    },                                                                  
    "delete": {                                                        
      "method": "delete",                                              
      "properties": [ ]                                                
    }                                                                   
  }                                                                     
}

Be aware thought that HAL-FORMS is designed to show only actions available against the same resource (or URI). In case of JSON representations, HAL-FORMS are assigned a dedicated media type application/prs.hal-forms+json.

3.3. JSON:API

JSON:API is one of the most widely supported standards for hypermedia-powered web services and APIs. It was originally drafted by Yehuda Katz back in 2013 and is growing in popularity ever since. As you may guess, it is solely applicable to JSON representation.

JSON:API is designed to minimize both the number of requests and the amount of data transmitted between clients and servers. This efficiency is achieved without compromising readability, flexibility, or discoverability.

https://jsonapi.org/format/

JSON:API specification describes semantics for links, resource relations and modification of resources (equivalent of actions). Plus, it covers the way to represent errors.

{                                                                                                             
  "data": {                                                                                                  
    "id": "13e1892765c5",                                                                                    
    "type": "reservation",                                                                                   
    "links": {                                                                                               
      "self": "https://rentals.jcg.com/reservations/13e1892765c5"
    },                                                                                                        
    "attributes": {                                                                                          
      "from": "2020-01-01",                                                                                  
      "to": "2020-01-05",                                                                                    
      "vehicle": "Honda Civic 2020"                                                                          
    },                                                                                                        
    "relationships": {                                                                                       
      "customer": {                                                                                          
        "links": {                                                                                           
          "self": "https://rentals.jcg.com/reservations/13e1892765c5/relationships/customer",              
          "related": "https://rentals.jcg.com/reservations/13e1892765c5/customer"                          
        }                                                                                                     
      }                                                                                                       
    }                                                                                                         
  }
}

What makes JSON:API to stand out is that fetching (query) patterns like sorting, filtering, sparse fieldsets and pagination are thought through and are part of the specification as well.

{
  "data": [
    {
      "id": "ce5886acbb87",
      "type": "reservation",
      "links": {
        "self": "https://rentals.jcg.com/reservations/ce5886acbb87"
      },
      "attributes": {
        "from": "2020-01-01",
        "to": "0120-01-12",
        "vehicle": "Volkswagen Golf 1.2 TSI"
      },
      "relationships": {
        "customer": {
          "links": {
            "self": "https://rentals.jcg.com/reservations/ce5886acbb87/relationships/customer",
            "related": "https://rentals.jcg.com/reservations/ce5886acbb87/customer"
          }
        }
      }
    },
    {
      "id": "fc14e8ef90f5",
      "type": "reservation",
      "links": {
        "self": "https://rentals.jcg.com/reservations/fc14e8ef90f5"
      },
      "attributes": {
        "from": "2020-01-10",
        "to": "2020-01-12",
        "vehicle": "BMW 325i"
      },
      "relationships": {
        "customer": {
          "links": {
            "self": "https://rentals.jcg.com/reservations/fc14e8ef90f5/relationships/customer",
            "related": "https://rentals.jcg.com/reservations/fc14e8ef90f5/customer"
          }
        }
      }
    }
  ],
  "links": {
    "first": "https://rentals.jcg.com/reservations?page[limit]=2",
    "last": "https://rentals.jcg.com/reservations?page[limit]=2&page[offset]=2",
    "next": "https://rentals.jcg.com/reservations?page[limit]=2&page[offset]=2"
  },
  "meta": {
    "totalResourceCount": 3
  }
}

Fairly speaking JSON:API has pretty simple, readable and understandable format. To achieve such trade-offs, it makes some opinionated decisions. For example, you will not see actions directly as part of the JSON:API document. They are in fact implicit and implied according to HTTP protocol: POST for creation, PATCH for modification, DELETE for removal. It may lead to some interpretation difficulties for clients, for example, where is PUT?

Although the version 1.0 of JSON:API specification had seen the light in 2015, it is being actively worked on and considered as an evolving document. JSON:API has properly registered media type designation application/vnd.api+json and also has own JSON Schema definition.

3.4. JSON-LD

JSON-LD, a JSON-based serialization for linked data, has gained the status of the official W3C candidate recommendation back in 2014. It has probably the most active community and the latest version of the specification, JSON-LD 1.1, was published literally a month ago. The key principles behind JSON-LD  design emphasize on ability to easily integrate into existing systems that already use JSON and augment them with JSON-LD semantics without breaking the established contacts.

It would be fair statement to say that JSON-LD is more about linked data than hypermedia but its rich capabilities to express the information, context and relations fit exceptionally well some of the hypermedia promises.

{                                                               
  "@context": {                                                
    "@vocab": "http://schema.org/"                             
  },                                                            
  "@type": "Reservation",                                      
  "id": "13e1892765c5",                             
  "vehicle": "Honda Civic 2020",                               
  "from": "2020-01-01",                      
  "to": "2020-01-05",                        
  "customer": {                                                
    "@id": "https://rentals.jcg.com/customers/fed195a03e9d"      
  },                                                            
  "@id": "https://rentals.jcg.com/reservations/13e1892765c5"     
} 

One of the drawbacks of JSON-LD is that it lacks support for actions. This major shortcoming of JSON-LD was addressed by Hydra, a vocabulary for hypermedia-driven web services and APIs.

The basic idea behind Hydra is to provide a vocabulary which enables a server to advertise valid state transitions to a client. A client can then use this information to construct HTTP requests which modify the server’s state so that a certain desired goal is achieved.

http://www.hydra-cg.com/spec/latest/core/#hydra-at-a-glance

To understand how exactly it works, let us take a look at the quick example of the JSON-LD document which has been enriched with Hydra semantics.

{
  "@context": {
    "@vocab": "http://schema.org/",
    "hydra": "http://www.w3.org/ns/hydra/core#"
  },
  "@type": "hydra:Collection",
  "hydra:collection": [ {
    "@type": "hydra:Collection",
    "@id": "https://rentals.jcg.com/reservations",
    "hydra:manages": {
      "hydra:property": "self",
      "hydra:subject": "https://rentals.jcg.com/reservations"
    },
    "hydra:operation": [ {
      "hydra:method": "GET"
    } ]
  } ],
  "hydra:member": [ {
    "@type": "Reservation",
    "vehicle": "Volkswagen Golf 1.2 TSI",
    "from": "2020-02-01",
    "to": "2020-02-12",
    "customer": {
      "@id": "https://rentals.jcg.com/customers/fed195a03e9d",
      "hydra:operation": [ {
        "hydra:method": "GET"
      } ]
    },
    "@id": "https://rentals.jcg.com/reservations/ce5886acbb87",
    "hydra:operation": [ {
      "hydra:method": "GET"
    }, {
      "hydra:method": "PUT",
      "hydra:expects": {
        "@type": "UpdateReservation",
        "hydra:supportedProperty": [ {
          "hydra:property": "from"
        }, {
          "hydra:property": "to"
        }, {
          "hydra:property": "vehicle"
        } ]
      }
    }, {
      "hydra:method": "DELETE"
    } ],
    "id": "ce5886acbb87"
  }, {
    "@type": "Reservation",
    "vehicle": "BMW 325i",
    "from": "2020-01-10",
    "to": "2020-01-12",
    "customer": {
      "@id": "https://rentals.jcg.com/customers/fed195a03e9d",
      "hydra:operation": [ {
        "hydra:method": "GET"
      } ]
    },
    "@id": "https://rentals.jcg.com/reservations/fc14e8ef90f5",
    "hydra:operation": [ {
      "hydra:method": "GET"
    }, {
      "hydra:method": "PUT",
      "hydra:expects": {
        "@type": "UpdateReservation",
        "hydra:supportedProperty": [ {
          "hydra:property": "from"
        }, {
          "hydra:property": "to"
        }, {
          "hydra:property": "vehicle"
        } ]
      }
    }, {
      "hydra:method": "DELETE"
    } ],
    "id": "fc14e8ef90f5"
  } ],
  "hydra:totalItems": 3,
  "hydra:view": {
    "@type": "hydra:PartialCollectionView",
    "hydra:next": "https://rentals.jcg.com/reservations?page=1&size=2",
    "hydra:first": "https://rentals.jcg.com/reservations?page=0&size=2",
    "hydra:last": "https://rentals.jcg.com/reservations?page=1&size=2"
  }
}

Just to reiterate one more time, JSON-LD’s strongest point is data linking. Combined with Hydra, your web services and APIs would get the full-fledged hypermedia capabilities however the integration may not be as easy as one may expect. The JSON-LD has a reserved media application/ld+json.

3.5. Siren

Siren, authored by Kevin Swiber in 2012, is a hypermedia specification for representing entities. An entity in Siren’s vocabulary is an URI-addressable resource that has properties, actions and navigable links associated with it. It is worth noting that Siren was designed specifically for web services and APIs, for example actions have a way to map directly to HTTP protocol verbs.

{                                                                                                    
  "class": [ "reservation" ],                                                                       
  "properties": {                                                                                   
    "id": "13e1892765c5",                                                                
    "from": "2020-01-01",
    "to": "2020-01-05", 
    "vehicle": "Honda Civic 2020"                                                                   
  },                                                                                                 
  "entities": [ {                                                                                   
    "rel": [ "customer" ],                                                                          
    "href": "https://rentals.jcg.com/customers/fed195a03e9d"                                          
  }, {                                                                                               
    "class": [ "customer" ],                                                                        
    "rel": [ "http://schema.org/customer" ],                                                        
    "properties": {                                                                                 
      "firstName": "John",                                                                          
      "lastName": "Smith",                                                                          
      "id" : "fed195a03e9d"                                                                  
    }                                                                                                
  } ],                                                                                               
  "actions": [ {                                                                                    
    "name": "update",                                                                               
    "method": "PUT",                                                                                
    "href": "https://rentals.jcg.com/reservations/13e1892765c5",                                      
    "fields": [ {                                                                                   
      "name": "from",                                                                               
      "type": "date"                                                                                
    }, {                                                                                             
      "name": "to",                                                                                 
      "type": "date"                                                                                
    }, {                                                                                             
      "name": "vehicle",                                                                            
      "type": "text"                                                                                
    } ]                                                                                              
  }, {                                                                                               
    "name": "delete",                                                                               
    "method": "DELETE",                                                                             
    "href": "https://rentals.jcg.com/reservations/13e1892765c5"                                       
  } ],                                                                                               
  "links": [ {                                                                                      
    "rel": [ "self" ],                                                                              
    "href": "https://rentals.jcg.com/reservations/13e1892765c5"                                       
  } ]                                                                                                
} 

Despite its age, Siren is still listed as work in progress. It is not as popular as HAL or JSON:API per se, but its relative simplicity and web API first semantics make it an option worth considering. The media type for Siren’s JSON representation is application/vnd.siren+json.

3.6. Collection+JSON

Collection+JSON specification, created by Mike Amundsen in 2011, aims to be a hypermedia type designed to support reading, writing and querying simple collections. It is somewhat inspired by The Atom Syndication Format (RFC-4287) and the The Atom Publishing Protocol (RFC-5023). One interesting fact about Collection+JSON is that it treats everything as a collection and as such, the single item is represented as a collection of one element.

{
  "collection": {
    "version": "1.0",
    "href": "https://rentals.jcg.com/reservations/13e1892765c5",
    "links": [ {
      "rel": "customer",
      "href": "https://rentals.jcg.com/customers/fed195a03e9d"
    } ],
    "items": [ {
      "href": "https://rentals.jcg.com/reservations/13e1892765c5",
      "data": [ {
        "name": "from",
        "value": "2020-01-01"
      }, {
        "name": "id",
        "value": "13e1892765c5"
      }, {
        "name": "to",
        "value": "2020-01-05"
      }, {
        "name": "vehicle",
        "value": "Honda Civic 2020"
      } ],
      "links": [ {
        "rel": "customer",
        "href": "https://rentals.jcg.com/customers/fed195a03e9d"
      } ]
    } ],
    "template": {
      "data": [ {
        "name": "from",
        "value": ""
      }, {
        "name": "to",
        "value": ""
      }, {
        "name": "vehicle",
        "value": ""
      } ]
    }
  }
}

As you may guess, Collection+JSON standard is a great fit for lists and collections. It also includes support for query templates (links and relations) and for write template (actions), along with standardized error reporting.

At some point Collection+JSON was quite popular but comparing to other alternatives, it is arguably more difficult to implement. Also, the bias towards “everything is a collection” is non-intuitive to deal with.

The media type for Collection+JSON is application/vnd.collection+json.

3.7. UBER

The UBER hypermedia type describes the support of the simple state transfers and ad-hoc transitions. It was co-created by Mike Amundsen (yes, the author of Collection+JSON) and Irakli Nadareishvili circa 2014 and targets both the XML and JSON variants.

So what is the motivation of the authors to come up with yet another hypermedia type? This tweet from Mike Amundsen brings some clarity on the reasoning.

Collection+JSON is a highly structured CRUD format. HAL is light on inline hypermedia, Siren has a rich obj model. UBER is wide-open & minimalist.

https://twitter.com/mamund/status/456508872832716800

UBER documents support link relations, actions and error reporting mechanism. The specification was designed to work with multiple protocols but includes detailed guidelines regarding the HTTP –based implementation and interpretation.

{
  "uber": {
    "version": "1.0",
    "data": [ {
      "name": "customer",
      "rel": [ "customer" ],
      "url": "https://rentals.jcg.com/customers/fed195a03e9d"
    }, {
      "name": "self",
      "rel": [ "self" ],
      "url": "https://rentals.jcg.com/reservations/13e1892765c5"
    }, {
      "name": "update",
      "rel": [ "update" ],
      "url": "https://rentals.jcg.com/reservations/13e1892765c5",
      "action": "replace",
      "model": "from={from}&to={to}&vehicle={vehicle}"
    }, {
      "name": "delete",
      "rel": [ "delete" ],
      "url": "https://rentals.jcg.com/reservations/13e1892765c5",
      "action": "remove",
      "model": ""
    }, {
      "name": "reservation",
      "data": [ {
        "name": "from",
        "value": "2020-01-01"
      }, {
        "name": "id",
        "value": "13e1892765c5"
      }, {
        "name": "to",
        "value": "2020-01-05"
      }, {
        "name": "vehicle",
        "value": "Honda Civic 2020"
      } ]
    } ]
  }
}

It still marked as a draft (stable draft to be precise) and not evolving much since 2016. It has no officially registered media type so in case of JSON representation, you have to use application/vnd.amundsen-uber+json.

3.8. Yahapi

There is a number of lesser known hypermedia specifications which nonetheless deserve the attention. We are going to start with Yet Another Hypermedia(ish) API specification (Yahapi), firstly published in 2014, which is essentially a list of hypermedia-enabling conventions.

In the end, Yahapi is just a list of preferences that make your API look nice, simple and consistent.

https://github.com/Yahapi/yahapi

Yahapi provides a list of guidelines for pagination, sorting, partial results and error formats, supports links and relations but unfortunately has no support for actions. It is rarely found in the wild and is not really active. The media type of a Yahapi document is just application/json.

3.9. Mason

Mason is a JSON-based format for introducing hypermedia elements into classic JSON data representations. In particular, it includes hypermedia elements for links and actions along with standardized error handling. Mason is registered in the media type registry as application/vnd.mason+json.

3.10. Ion

Ion positions itself as an intuitive JSON-based hypermedia type for REST. It covers both the links with relation types and actions using forms. Unfortunately, the specification appears to be in a dormant state since 2018. The media type assigned for Ion content is application/ion+json.

4. The Costs of HATEOAS

At this point you should have a pretty good idea about the role of hypermedia and HATEOAS when applied to the RESTful web service and APIs. And if you are a seasoned software developer working on typical enterprise projects, you may hardly recall when you have run into HATEOAS the last time. Let us face the truth: nobody knows how to use hypermedia.

In the context of REST architecture, HATEOAS is a must, but it is not free and incurs costs, sometimes quite significant. And not only in terms of implementation but in terms of upfront design as well. In this regard, servers are relatively easy but clients are really hard (at best you may get support of the Link header). In practice it means that even if you develop perfectly RESTful web service or API, the chances are high that the other developers would choose to not utilize hypermedia in their clients at all.

Along this part of the tutorial we have talked about a dozen of different hypermedia specifications. With a few exceptions, most of them bear “work in progress” or “unstable” labels. In general it means that there are still dark waters to navigate and the chances you end up there are high. This is one of the reasons why new specifications are being created on an ongoing and regular basis. And obviously, each specification require very different amount of design and implementation efforts. Hopefully your programming language or platform ecosystem already has some libraries and frameworks to get you started, but in general this is not the case.

In terms of the data exchange between servers and clients, hypermedia may lead to more roundtrips in order to fetch the additional details behind links and relations. It may also cause the resource representations to grow in size considerably due to the need to include links and actions.

The interesting question you might be asking yourself how the REST architectural style, in particular HATEOAS, mingles with microservices architecture? To illustrate the problem, consider a system with just two microservices, Customer Service and Reservation Service. Since the clients do not require any additional or up-front knowledge, how would they discover there are multiple services? And how the Reservation Service would incorporate hypermedia elements related to the customers and vice versa? It is very likely the responsibility of another layer like API gateways or / and aggregators and certainly does not sound like a simple problem.

If at this point you are scared of hypermedia and HATEOAS, fear not. The benefits greatly outweigh the required costs and efforts, especially in the long run. As the confirmation of that, let us go over short and simple case study.

5. The Case Study

The sample application we are going to dissect is a car rental platform which at the moment implements only two RESTful web APIs to manage reservations and customers. The only knowledge shared with any client is an entry point into the platform, for demonstration purposes let as assume there is a server behind a fabricated URL https://rentals.jcg.com.

This endpoint is only accepting HTTP GET request and returns the hypermedia document (using HAL along with HAL-FORMS), the example is shown below.

$ curl https://rentals.jcg.com/
{                                                        
  "_links": {                                           
    "self": {                                           
      "href": "https://rentals.jcg.com/"                  
    },                                                   
    "reservations": {                                   
      "href": "https://rentals.jcg.com/reservations"      
    },                                                   
    "customers" : {                                      
      "href": "https://rentals.jcg.com/customers"         
    }                                                    
  }                                                      
}

Once the hypermedia aware client receives such document, it could clearly understand that there are two links it could navigate from there, reservations and customers. In this case, the client is interested in reservations so it goes there.

$ curl -iv https://rentals.jcg.com/reservations
{
  "_embedded": {
    "reservations": [ {
      "id": "ce5886acbb87",
      "vehicle": "Volkswagen Golf 1.2 TSI",
      "from": "2020-02-01",
      "to": "2020-02-12",
      "_links": {
        "customer": {
          "href": "https://rentals.jcg.com/customers/fed195a03e9d"
        },
        "self": {
          "href": "https://rentals.jcg.com/reservations/ce5886acbb87"
        }
      },
      "_templates": {
        "default": {
          "method": "put",
          "properties": [ {
            "name": "from",
            "regex" : "yyyy-MM-dd",
            "required": true
          }, {
            "name": "to",
            "regex" : "yyyy-MM-dd",
            "required": true
          }, {
            "name": "vehicle",
            "required": true
          } ]
        },
        "delete": {
          "method": "delete",
          "properties": [ ]
        }
      }
    }, 
    ...
    ]
  },
  "_links": {
    "self": {
      "href": "https://rentals.jcg.com/reservations"
    }
  },
  "_templates": {
    "default": {
      "method": "post",
      "properties": [ {
        "name": "from",
        "regex" : "yyyy-MM-dd",
        "required": true
      }, {
        "name": "to",
        "regex" : "yyyy-MM-dd",
        "required": true
      }, {
        "name": "vehicle",
        "required": true
      } ]
    }
  }
}

This time the server returns beefy resource representation (for simplicity, there is only one reservation item kept in the collection) with many hypermedia elements so the client has several choices to pick from.

For example, by inspecting the _templates hypermedia element it figures out that it could create a new reservation by submitting HTTP POST request with from, to and vehicle properties in payload (since it is  HAL-FORMS, using application/x-www-form-urlencoded form encoding). You may notice that although there are some constraints, there is no any indication what is the type (string? date? number?) of the from, to or vehicle properties.  

"_templates": {
  "default": {
    "method": "post",
    "properties": [ {
      "name": "from",
      "regex" : "yyyy-MM-dd",
      "required": true
    }, {
      "name": "to",
      "regex" : "yyyy-MM-dd",
      "required": true
    }, {
      "name": "vehicle",
      "required": true
    } ]
  }
}

Alternatively, the client may express an interest in particular reservation, by introspecting the associated _templates hypermedia element.

"_templates": {
  "default": {
    "method": "put",
    "properties": [ {
      "name": "from",
      "regex" : "yyyy-MM-dd",
      "required": true
    }, {
      "name": "to",
      "regex" : "yyyy-MM-dd",
      "required": true
    }, {
      "name": "vehicle",
      "required": true
    } ]
  },
  "delete": {
    "method": "delete",
    "properties": [ ]
  }
}

In this case, the server provides the choice to update the reservation using the HTTP PUT request (full replace semantics) or remove the particular reservation altogether using the HTTP DELETE request.

6. RESTful services with HATEOAS. Hypermedia – Conclusions

In this part of the tutorial we have talked about hypermedia and HATEOAS, an integral part of any RESTful web service or APIs. The landscape of the hypermedia specifications is not settled in stone and is constantly changing. We went over quite a lot of options but did not stumble upon clear winner. The reason for that is that each one makes different trade-offs and you would need to decide which hypermedia specification fits the best to the needs of your application. The context matters so please take it seriously. You may find the article On choosing a hypermedia type for your API – HAL, JSON-LD, Collection+JSON, SIREN, Oh My! by Kevin Sookocheff to be of great help here.

7. What’s next

In the next section of the tutorial we are going to talk about the role of the documentation in the life of the  RESTful web services and APIs, powered by hypermedia.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
3 years ago

Great job, Andrey! Thank you very much!
I think the next logical step would be the explanation of the API client state machine.
Probably, it makes sense to break it into two scenarios:
– API client as a backend of a User Interface.
– API client as a part of a program that must automate all management steps.

Andriy Redko
3 years ago
Reply to 

Thank you very much for the feedback! Indeed, there are other parts of the tutorial in works (at least three of them). The client part is going to come back once again, applied in the context of the project (both for UI-driven and API-driven use cases) so we could actually see it backed by existing tooling. Thank you.

Back to top button