Enterprise Java

RESTful services with HATEOAS. Documenting Hypermedia APIs

1. Introduction

Hopefully the previous part of the tutorial not only unveiled the far-reaching implications of the hypermedia and HATEOAS but has truly convinced us that these are among the fundamental building blocks of the RESTful web services and APIs. In this part we are going to continue this theme with the focus on the documentation aspect in order to address the question of how the capabilities of the web service or API could be communicated upfront.

Before going any further, let us detail the problem at hand from the perspectives of service provider and service consumer, according to REST architectural style. Essentially, the discoverability aspect of the hypermedia-driven web services and APIs allows the service provider to publish a single URL, the entry point, and be done with that. On the other side, any hypermedia-aware consumer should be able to navigate through the resources using the contextual links and accomplish any desired state modifications along the way if necessary. The only prerequisite is that providers and consumers should speak the same hypermedia dialect. This is a machine perspective on provider / consumer interactions, an example of the exploratory client.

Now, let us look at this problem from a developer perspective. How would you know what are the properties of a particular resource? How would you know what type of links and link relations to expect? And what about the kind of actions or affordances it supports? What side effects they may lead to? At the end, as a developer you just need to fulfill a simple requirement like “Customer C wants to extend its reservation R to one more week”.  This is a human perspective on provider / consumer interactions.

A lot of challenging questions, but do we have the answers to them? Let us find out …

2. OpenAPI and Friends

Nowadays, if we look around at what is the recommended way to document your RESTful web services and APIs, you undoubtedly are going to run into OpenAPI specification. There are several versions of the specification in the wild but the latest one as of the moment of this writing is 3.0.2.

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md

To be fair, OpenAPI is a great specification, it is the result of the outstanding collaborative efforts of many organizations and individuals. It has been widely accepted as the go-to approach for documenting RESTful web services and APIs but it is not the only one out there. Other good specifications like RAML, API Blueprint, RSDL, WADL, API Builder, iodocs and tinyspec are also in use and worth to know about.

OpenAPI and friends do the job, but on a fundamental level, these specifications are centered on resource locations and have little or no hypermedia support.

As far as it gets, hypermedia completely ruins resource locations. The hypermedia-driven RESTful web services and APIs are not static, far from that, mostly everything a machine or human needs to know is shared at runtime.

3. OpenAPI is not the answer, now what?

You may often run into the claims that hypermedia transforms RESTful web services and APIs into the state machines around resources. And at conceptual level, it makes sense since hypermedia brings in a clearly defined semantics of states and transitions.

Instead of resource locations, we should be focusing on describing resources themselves. At the very least, the resource description should include its data semantics, possible actions (state transitions) which are supported (depending on the context if possible) and related navigable resources (links with relation types).

3.1. Application-Level Profile Semantics (ALPS)

Application-Level Profile Semantics (ALPS) is probably the most advanced and embraced specification to document and share the semantics of hypermedia in context of RESTful web services and APIs.

An ALPS document can be used as a profile to explain the application semantics of a document with an application-agnostic media type (such as HTML, HAL, Collection+JSON, Siren, etc.). This increases the reusability of profile documents across media types.

http://www.alps.io/spec/index.html

The role of the profiles is to establish a shared and structured vocabulary, both for humans and machines. In the nutshell, the profiles allow to define the semantics of the data and transitions along with ability to include documentation.

When implementing a hypermedia client/server application using a general media type (HTML, Atom, Collection+JSON, etc.), client and server instances need to share an understanding of domain-specific information such as data element names, link relation values, and state transfer parameters. This information is directly related to the application being implemented (e.g. accounting, contact management, etc.) rather than the media type used in the representations.

http://www.alps.io/spec/index.html

In the previous part of the tutorial we have seen a number of specifications to implement RESTful web services and APIs, let us try to augment one of those (for example, HAL) with ALPS support.

$ 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"
    },
    "profiles": {
      "href": "https://rentals.jcg.com/alps"
    }
  }
}

Comparing to the example from the case study, you may notice there is one additional link profiles returned by the server. Let us look behind it.

$ curl https://rentals.jcg.com/alps
{                                                                
  "version": "1.0",                                             
  "doc": {                                                                                    
    "href": "https://rentals.jcg.com/documentation.html"        
  },                                                             
  "descriptor": [ {                                             
    "id": "customers",                                           
    "href": "https://rentals.jcg.com/alps/customers"               
  }, {                                                           
    "id": "reservations",                                        
    "href": "https://rentals.jcg.com/alps/reservations"            
  } ]                                                            
}

So far we have advertised two profile descriptors, customers and reservations. Let us dig deeper into reservations profile descriptor.

$ curl https://rentals.jcg.com/alps/reservations
{                                            
  "version": "1.0",                         
  "descriptor": [ {                         
    "id": "reservation",                    
    "type": "SEMANTIC",                     
    "descriptor": [ {                       
      "id": "from",                         
      "type": "SEMANTIC"                    
      "href": "https://schema.org/Date"
    }, {                                     
      "id": "id",                           
      "type": "SEMANTIC"                    
      "href": "https://schema.org/Thing#identifier"
    }, {                                     
      "id": "to",                           
      "type": "SEMANTIC",
      "href": "https://schema.org/Date"
    }, {                                     
      "id": "vehicle",                      
      "type": "SEMANTIC",
      "href": "https://schema.org/Vehicle#name"
    } ]                                      
  }, {                                       
    "id": "create",                           
    "name": "reservations",                 
    "type": "UNSAFE",                         
    "rt": "#reservation"                    
  }, {                                       
    "id": "list",                           
    "name": "reservations",                 
    "type": "SAFE",                         
    "rt": "#reservation"                    
  }, {                                       
    "id": "customer",                       
    "name": "customer",                     
    "type": "SAFE",                         
    "href": "https://rentals.jcg.com/alps/customers"
    "rt": "#customer"                       
  }, {                                       
    "id": "update",                         
    "name": "reservation",                  
    "type": "IDEMPOTENT",                   
    "rt": "#reservation"                    
  }, {                                       
    "id": "delete",                         
    "name": "reservation",                  
    "type": "IDEMPOTENT",                   
    "rt": "#reservation"                    
  } ]                                        
}                       

It is worth taking some time and briefly go over this profile. The first thing it outlines is reservation descriptor to describe the data semantics (properties and their types). Afterwards, the number of descriptors for actions and transitions comes in (create, list, delete, update, customer).

As a side note, although in our examples the profiles are crafted by the applications, the ALPS also has a notion of registry (ALPS Profile Registry) to allow profile definitions to be reused and referenced. A large set of ALPS profiles is already pre-built from schema.org definitions and available at http://alps.io/schema.org.

Back to the subject, let us take a look at how ALPS profiles penetrate into resource representations, using HAL style as the reference.

{                                                                                            
  "id": "13e1892765c5",                                                                     
  "vehicle": "Honda Civic 2020",                                                            
  "from": "2020-01-01",                                                                     
  "to": "2020-01-05",                                                                       
  "_links": {                                                                               
    "profile": {                                                                               
      "href": "https://rentals.jcg.com/alps/reservations#reservation"                                      
    },                                                                                       
    "customer": {                                                                           
      "href": "https://rentals.jcg.com/customers/fed195a03e9d",
      "profile": "https://rentals.jcg.com/alps/customers#customer"
    },                                                                                       
    "self": {                                                                               
      "href": "https://rentals.jcg.com/reservations/13e1892765c5"                             
    }                                                                                        
  },                                                                                         
  "_templates": {                                                                           
  …
  }                                                                                          
}

The HAL document has been enriched to include a profile link relation which points to the respective resource profile. Alternatively, sometimes the Links header could be seen useful to send back the resource profile information.

Links: profile="https://rentals.jcg.com/alps/reservations#reservation"

Overall, ALPS specification is quite simple and human-friendly way to describe the resource semantics and transitions for RESTful web services and APIs.

3.2. ALPS vs JSON-LD, …

Some of the hypermedia specifications have first class support for data semantics (vocabularies). A great example is JSON-LD with the @vocab contextual definition.

{                                                               
  "@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"     
}

When relying on hypermedia specifications, the resource semantics details come as a part of the resource representation. By all means, this is valuable information. On the contrary, ALPS profiles exist separately of the resource representations and media types, and could be queried and interpreted independently.

3.3. JSON Hyper-Schema

JSON Schema is a JSON-based format for describing the structure of the JSON data. JSON Hyper-Schema specification enriches the JSON Schema vocabulary with the ability to specify hyperlinks and hypermedia-related keywords.

JSON Hyper-Schema is a JSON Schema vocabulary for annotating JSON documents with hyperlinks and instructions for processing and manipulating remote JSON resources through hypermedia environments such as HTTP.

https://tools.ietf.org/html/draft-handrews-json-schema-hyperschema-02

In comparison to ALPS, JSON Hyper-Schema offers significantly richer semantics for describing data, links (relations) and actions (affordances). The example below is just one of the possibilities to craft JSON Hyper-Schema for reservation resource.

{
    "title": "Reservation",
    "type": "object",
    "properties": {
        "id": {
            "title": "Unique Reservation identifier",
            "type": "string"
        },
        "vehicle": {
            "title": "Name of the vehicle",
            "type": "string"
        },
        "from": {
            "title": "Start date",
            "type": "date"
        },
        "to": {
            "title": "End date",
            "type": "date"
        }
    },
    "required" : ["from", "to", "vehicle"],
    "links": [
        {
            "rel": "self",
            "href": "{id}"
        },
        {
            "title": "Customer",
            "rel": "customer",
            "href": "/reservation/{id}/customer"
        },
        {
            "title": "Cancel Reservation",
            "rel": "delete",
            "href": "{id}",
            "method": "DELETE"
        },
        {
            "title": "Update Reservation",
            "rel": "update",
            "href": "{id}",
            "method": "PUT",
            "schema": {
                "type": "object",
                "properties": {
                    "vehicle": {
                        "title": "Name of the vehicle",
                        "type": "string"
                    },  
                    "from": {
                        "title": "Start date",
                        "type": "date"
                    },
                    "to": {
                        "title": "End date",
                        "type": "date"
                    }  
                },
                "required": ["vehicle", "to", "from"]
            }
        }
    ]
}

Probably the first thing which stands out is the level of details each link and action has. One of the immediate drawbacks you may notice is that the semantics (for example, delete action) is interleaved with concrete pointers (like href templates).

3.4. Microformats

Besides ALPS and JSON Hyper-Schema, there are lesser known specifications which nonetheless deserve to be mentioned. Microformats is one of those.

Designed for humans first and machines second, microformats are a set of simple, open data formats built upon existing and widely adopted standards. Instead of throwing away what works today, microformats intend to solve simpler problems first by adapting to current behaviors and usage patterns.

http://microformats.org/wiki/about

The latest version of the microformats is called microformats2.

Microformats2 replaces and supersedes both classic microformats (sometimes called microformats1), as well as incorporates lessons learned from microdata and RDFa.

http://microformats.org/wiki/microformats2

The primary target of the microformats is HTML media type (semantic HTML) so it may not be the best fit to majority of the RESTful web services and APIs.

3.5. Dublin Core Metadata Initiative (DCMI)

The original Dublin Core specification, published back in 1995, is a set of fifteen (initially thirteen) core elements for describing resource semantics. Consequently, Dublin Core Metadata Initiative (DCMI) is an organization with a goal to support innovation in metadata design and best practices across a broad range of purposes and business models. It defines a richer specification, DCMI Metadata Terms.

By and large, Dublin Core is one of the pioneering efforts focused on application profiles and linked data (especially based on RDF vocabularies).  The number of the specifications it governs has grown significantly over the years, going beyond just metadata.

As with microformats, the broad scope of Dublin Core and related specifications may not be a first choice you will think of for RESTful web services and APIs.

4. Conclusions

Along this part of the tutorial we have discussed the challenges of documenting RESTful web services and APIs with rich hypermedia support. To be fair, the claims this is a solved problem are far from the truth. The OpenAPI specification, being a de-facto choice these days, does not embrace hypermedia as the first class citizen yet. The most prominent alternatives, notably Application-Level Profile Semantics (ALPS) and JSON Hyper-Schema, are certainly moving things forward in right direction, however both are still being considered as work in progress.

5. What’s next

In the next part of the tutorial we are going to shift from theory to practice by designing and implementing the full-fledged RESTful web service API on JVM platform (using Java).

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.

0 Comments
Inline Feedbacks
View all comments
Back to top button