The Hypermedia Hitchhiker's Guide to Standardised Affordance Metadata
Tags: openapi, hypermedia, http, rest
Published: 2018-01-22 19:00:07

---
title: The Hypermedia Hitchhiker's Guide to Standardised Affordance Metadata
published: true
description: A modest proposal to bridge the gap between REST and OpenAPI
tags: openapi,hypermedia,http,rest
---

> REST is asking Siri "What can you do?" RPC is lookup up a list of commands from osxdaily or trying to guess.

#### [Phil Sturgeon](https://twitter.com/philsturgeon/status/955371535073767425)

![Babel fish](https://s-media-cache-ak0.pinimg.com/originals/03/9b/93/039b936b15253a16ed6537c0e3b01d0e.gif)

Let's extrapolate from that a little.

What is Siri's response likely to be, and what would that look like from a REST API?

## Siri: What can you do?

Let's bang the root of that hoopy API and see if we get back anything useful.

`GET /`

Holy swutting Belgium man!

```json
{
    "_links": [
        {
            "rel": "self",
            "href": "/"
        },
        {
            "rel": "siri:whats-playing",
            "href": "/nowplaying"
        },
        {
            "rel": "siri:play-song",
            "href": "/nowplaying/{songName}"
        },
        {
            "rel": "siri:send-message",
            "href": "/messages/{recipient}/{message}"
        },
        {
            "rel": "siri:buy-book-by-title",
            "href": "/purchases/book/{title}"
        },
        {
            "rel": "siri:buy-book-by-title-and-author",
            "href": "/purchases/book/{title}/by/{author}"
        }
    ]
}
```

By the way, being a Linux user, I naturally had to get the information for the list above from [OSXDaily](http://osxdaily.com/2016/09/28/use-mac-siri-commands/), which we can think of as a static API description document (a la [the OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification)), after having to google for it, which we can think of as some kind of API discovery mechanism / registry lookup.

The perennial question now is *how* do we perform those link relation actions?

> “Accessing any hypermedia API in the sophisticated Eastern spiral arm of the galaxy tends to pass through three distinct and recognizable phases, those of Location, Enquiry and Confusion, otherwise known as the Where, What, and How phases. For instance, the first phase is characterized by the question 'Where is this API?' the second by the question 'What can I do with this API?' and the third by the question 'Oh I give up, how am I supposed to know what to do next?” 

#### With apologies to Douglas Adams

It may be obvious that a `GET` request to `/nowplaying` will return information on the currently-playing piece of Bach, but to rock out to some Procol Harum, do I `POST` to `/nowplaying/whiter%20%shade%20of%20pale`, or possibly `PUT` or even `PATCH` the resource representation?

If I want to buy a book by a particular author (I want the original "[Last Chance to See](https://www.amazon.co.uk/Last-Chance-Douglas-Adams-1991-12-31/dp/B019TM0JNC/ref=sr_1_4?s=books&ie=UTF8&qid=1516646527&sr=1-4&keywords=last+chance+to+see)" by Douglas Adams and Mark Cawardine, not the [sequel](https://www.amazon.co.uk/Last-Chance-to-See/dp/B0041T4NF4/ref=sr_1_7?s=books&ie=UTF8&qid=1516646527&sr=1-7&keywords=last+chance+to+see) by Mark C. and Stephen Fry), can I pass both authors? Do I concatenate them? Do I use an array?

What if I could consult the Hitchhiker's Guide to the API?

Obviously, out-of-band documentation is a REST no-no, but what about in-band documentation?

What if there was a standardised way of retrieving metadata about a resource... such as:

`OPTIONS /nowplaying/:songName`

Which returned a minimal, dynamically-generated OpenAPI definition for the given endpoint (including any necessary supporting definitions, such as parameters, headers, responses etc).

The OpenAPI format is used to describe the affordances of the endpoint, and the `Allow` response header gives us the supported HTTP methods for the endpoint to act as indices into the OpenAPI [pathItem object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#path-item-object).

The affordances metadata can be effectively cached by using the `ETag` and `If-None-Match` headers.

If the metadata hasn't changed since the last time the client checked, the API should respond to the `OPTIONS` request with HTTP [412](https://httpstatuses.com/412) - "Precondition Failed" as per [RFC7232](https://tools.ietf.org/html/rfc7232#section-3.2).

Obviously, other API description formats could be returned based on content-type negotiation.

An alternative to using the `OPTIONS` method is to include a link with the relation type of [service-desc](https://tools.ietf.org/html/rfc8631#section-4.2), and link your partial OAS definition that way.

If you want to play with returning partial OpenAPI definitions, I've knocked together a proof-of-concept utility in Node.js - [openapi-extract](https://github.com/Mermade/openapi-extract). It works with OpenAPI (fka Swagger) 2.0 and 3.0.x.

### References

#### Aka there's nothing new under the sun

* http://zacstewart.com/2012/04/14/http-options-method.html
* https://www.iana.org/assignments/link-relations/link-relations.xhtml
* http://json-schema.org/latest/json-schema-hypermedia.html