Hyper - Foundational Hypermedia Type

Description

Hyper is a media type optimized for easy production and consumption of hypermedia messages. Developers can write code against Hyper messages, and by using implementations of the Representor pattern, such as Kokua in Node, have their messages become consumable in many popular hypermedia formats, such as: HAL, Siren, UBER etc. Likewise, on the client side, you will be able to consume APIs written in multiple formats, while only coding against Hyper:

Hyper in Hub and Spoke

Hyper allows implementations of the Representor pattern to leverage the Hub and Spoke pattern and thus avoid the combinatorial explosion of complexity in the alternative, point-to-point approach.

Due to its simplicity and versatility, Hyper is also a very fine choice as the primary format of an API, but it’s entirely up to the API producer and the consumer whether they choose to do that or translate Hyper to another format, such as HAL or UBER.

Specification Lead:
Irakli Nadareishvili
Authors:
Irakli Nadareishvili, Randall Randall
Status:
Approved
Dates:
2017-12-16 (Created)
2018-03-06 (IANA Approval)
Proposed IANA Mime Type:
application/vnd.hyper+json
Supported H-Factors:
[LE], [LO], [LT], [LN], [LI], [CR], [CU], [CM], [CL]

Contents

  1. Design Choices
    1. Default Mixin - Core Vocabulary
    2. CURIEs and URI Templates
  2. Extensions
  3. Core Vocabulary
    1. h:head
    2. h:ref
    3. h:link
    4. h:value
    5. h:label
    6. h:type
    7. h:pvt
    8. Name
  4. An Example, Somewhat Full-Featured Hyper Document
  5. Important HTTP Headers
    1. Profile
    2. Content-Language
NOTE:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.

Design Choices

Primary purpose of Hyper (application/vnd.hyper+json) is to be used by developers as the hypermedia type that they actually code against. As such, simplicity and developer experience are very important in its design. Hyper stays as close as possible to plain JSON and avoids being verbose or bloated in its syntax.

Simplicity comes from an intentionally small set of additional constraints that Hyper introduces on top of “plain old JSON”. The Hyper spec is essentially as short as the following three principles:

  1. Hyper is a JSON document of arbitrary shape. Any JSON is a valid Hyper as long as it doesn’t violate the other two rules (which most JSON documents, do not).
  2. Keys of JSON objects in a Hyper document (referred to as ‘attributes’ in the rest of this spec, for brevity) MAY be URIs. In Hyper, attributes that are valid URIs indicate extra semantics and processing rules that MAY be provided by a resource at the other end of the URI. URIs do not have to be dereferenceable, however. They are just a namespace, and the meaning of a namespace CAN be provided by any convenient means (e.g. publishing an RFC, Swagger document or a nicely printed book). These URIs can also be Compact URIs - CURIEs. Namespacing attributes by providing them as URIs is a primary way of introducing additional semantic and processing rules - foundation of extensibility in Hyper.
  3. Applications producing and consuming Hyper messages acknowledge the implicit existence of a core set of extensions (‘core vocabulary’), that is documented at a set of URIs, the root of which is http://hyperjson.io/props/.

Hyper allows gradual introduction of Hypermedia controls in the form of mixins. As opposed to many other hypermedia formats, there is no required object shape in Hyper. Most valid JSON documents are also valid Hyper documents. Additional functionality is introduced through extensions. Extensions, in Hyper, add functionality via mixins, rather than inheritance-style implementation of a parent, abstract Hyper document. It is entirely up to individual API developers to decide “how much” hypermedia they want to add to their JSON payloads.

Default Mixin - Core Vocabulary

In Hyper, core vocabulary is mixed-in via the default extension. The default extension is {"prefix" : "h", "uri" : "http://hyperjson.io/props/"} that is always present and cannot be removed or overriden. Capabilities in the default extension form a small but powerful vocabulary selected based on the guidelines outlined in the Hypermedia Project Charter.

Hyper is a web standard, based on existing specifications, such as: URI Template [RFC6570], CURIEs and IANA-registered Link Relation Types.

CURIEs and URI Templates

CURIEs and URI Templates are used extensively in Hyper. If you are unfamiliar with them, it is worth spending a minute explaining them.

CURIEs are just a standard way of abbreviating long URIs by assigning prefix to the common part of the URI, similar to how namespaces are used in XML. If you are using the same base URI repeatedly in your API, using CURIES can significantly clean-up the payload and make it much more readable. Being a hypermedia format, we use URIs extensively in Hyper so keeping things clean and manageable is very useful.

Case in point: let’s assume that we need to refer to a lot of URIs that all start with “http://hyperjson.io/props/”. We can CURIE this URL by defining a prefix such as: {"h" : "http://hyperjson.io/props/"} and then long URIs like http://hyperjson.io/props/ref and http://hyperjson.io/props/link become much simpler h:ref and h:link. You can read more about CURIE standard at: https://www.w3.org/TR/2010/NOTE-curie-20101216/

URI Template is a web standard that allows describing a range of Uniform Resource Identifiers (URIs) through variable expansion. For instance http://api.example.com/users{user-id}/photo can describe a template for how a photo URI of any user may look in some API. You can read more about URI templates in RFC6570

Extensions

Hyper allows extending plain old JSON [RFC7159] through attributes that are CURIEd URIs. Most object key names in your JSON payload are simple strings. They only provide semantic meaning within the context of a particular message or an application. In Hyper, the key names of JSON objects (‘attributes’) can also be CURIEd URIs and have a meaning that is defined universally.

For instance, let’s look at this simple Hyper document:

{
  "firstname": "Laila", "lastname": "Stanton",
  "h:ref": {"about": "http://example.com/users/2334"}
}

Looking at this message alone, given no other documentation, we cannot be certain about the semantic meaning of “firstname” and “lastname” properties. They are probably defined in an API documentation somewhere, or somehow other communicated between the API producer and the consumer. However, knowing that the message is a Hyper document, we can learn quite a bit about the h:ref property:

  1. It’s a CURIE, with prefix “h”.
  2. In Hyper documents, “h” prefix is predefined and stands for http://hyperjson.io/props/, therefore the full URI form of h:ref attribute is: http://hyperjson.io/props/ref
  3. Looking-up documentation at http://hyperjson.io/props/ref we can learn that this property defines a hyperlink, and can point to other APIs, web pages, files, locations within the same API, email addresses, or any other URI. Hyperlinks in Hyper documents are also required to define a relationship type which “specifies the relationship of the target object to the link object”

In addition to using, an optionally CURIed, URIs as attributes, the semantic meaning of key names in JSON objects of a Hyper document can also be provided by attaching an ALPS profile, in HTTP header, as explained further in the spec.

In Hyper documents, appplication- and context-specific semantics SHOULD be provided by profile links, whilst CURIEd attributes shoud be used for generic and reusable extensions.

Core Vocabulary

Despite its flexible and extensible nature, Hyper is a very simple media type with a small vocabulary.

h:head

Optional. Used to define a header of a Hyper message. Fields in the header section apply to the entire message. There SHOULD be only one h:head property at the top level. All subsequent h:head properties after the first one, or the ones at deeper levels MUST be ignored.

h:head property points to a JSON object that MAY contain following optional fields:

version

optional. Version property indicates the version of the Hyper specification that a message represents. Currently, there’s only one version of the specification: 1.0, the current version. As such, the version property, if present, MUST be set to 1.0, until there’s another version of the specification. If the version field is omitted, it should be assumed to be 1.0.

title

optional. Title of the document

curies

optional. A JSON object where keys are prefixes and values are URIs of corresponding CURIEs, complying to the W3C definition. In Hyper, CURIEs can be used in values of “uri” fields or as any object’s key’s name. Please note that CURIEs can conflict with many valid uris, such as the ones starting with prefixes like “mailto:”, “git:”, “urn:”, “about:” etc. When Hyper sees a colon (“:”) in a key name or in a value of uri field, it considers the string to be a URI, unless it is a CURIE registered in the document. Case in point: if you define a CURIE with the prefix “data”, you will stop otherwise legit URIs of the form “data:” to be parsed as URIs, in your Hyper message. At such a point you may want to reconsider the prefix chosen. You can find the full list of possibly conflicting prefixes by looking at the IANA URI Schemes Registry.

Important: Hyper registers a special CURIE "h": "http://hyperjson.io/props/" that is always present and cannot be removed or overriden.

Example use of h:head in a Hyper document:
{
  "h:head": {
    "version": "1.0",
    "title": "Department Employees",
    "curies": {"ex": "http://api.example.com/"},
  },
  "department" : {
    "name" : "North-East Region",
    "h:ref" : { "about" : "ex:regions/north-east"}
  }
}

h:ref

Points to a JSON object where keys are link relationship types and values are corresponding URIs.

Example Document with h:refs:
{
  "h:head" {
    "title" : "Employees of North-East Department"
  },
  "department" : {
    "h:value" : "north-east",
    "h:label" : "North-East",
    "h:ref" : {"about" : "http://api.example.com/departments/1234"}
  },
  "employees" : [

  ],
  "h:ref" : {
    "self"  : "http://api.example.com/users?dep=1234&page=4",
    "next"  : "http://api.example.com/users?dep=1234&page=5",
    "prev"  : "http://api.example.com/users?dep=1234&page=3",
    "first" : "http://api.example.com/users?dep=1234&page=1",
    "last"  : "http://api.example.com/users?dep=1234&page=10"
  }
}

h:link is a super-set of h:ref allowing representation of complex hyperlinks and parametrized actions (similar to HTML’s Form element).

h:link points to an array of objects, where the order of such links should not be considered significant.

Each object can have following properties of predefined semantic meaning:

uri

required. A URI [RFC7320] or URI Template [RFC6570], or a CURIE which expands into a URI or URI Template.

template

optional. If this field is present, the value of the uri field should be treated as a URI Template [RFC6570] after any CURIE expansion. When present, the template attribute is an object containing the following properties:

  1. contentType - a media-type that should be used to encode fields into, to satisfy the construction of a request. E.g. “application/x-www-form-urlencoded”
  2. fields - an objects that explains what data elements should be submitted to the endpoint represented by uri. Each key is the name of the variable to be sent. If variables are going both in URI template and in the body of the request, and disambiguiation is required choose names sensibly to avoid conflicts. Each field object can have following properties:

    1. label : optional. Hint for human-friendly label to present to humans, if an input form is rendered.
    2. required : optional. Whether a value is required. Boolean true/false. Default: true.
    3. type : optional. One of: “text”, “number”, “date”, “hidden” or “boolean”. Defaults to “text”.
    4. default : optional. Default value.
    5. pattern: optional. Restrictions on allowed values. Syntax must be the same as used in pattern in HTML5
rel

Required. An array of link relation types per RFC8288. Only IANA-registered link relation types SHOULD be refered to by simple names, all application-specific and non-common link relation types should be URIs (usually CURIEed, to save space). The order of the link relations in this array should not be considerd significant.

label

optional. String. Usually human-readable, for presentation hints.

embed

optional. Suggests to the consumer whether the resource from the URI SHOULD be embedded within the currently loaded document or treated as a transition to a new state/document (embed=”false”). Defaults to false. Unrecognized values should also be treated as false.

Expected values include the folowing list:

action

Optional. String. Indicates the action represented by the uri transition. Hyper borrows definitions of possible action types from UBER and the list of valid values is as follows:

  1. append : An unsafe, non-idempotent request to add a new item. (e.g HTTP POST)
  2. partial : An unsafe, non-idempotent request to modify parts of an existing item. (e.g. HTTP PATCH)
  3. read : A safe, idempotent request. [DEFAULT] (e.g. HTTP GET)
  4. remove : An unsafe, idempotent request to delete an existing item. (e.g. HTTP DELETE)
  5. replace : An unsafe, idempotent request to replace an existing item. (e.g. HTTP PUT)
{
  "h:link": [{
    "uri" : "http://api.example.com/users/{user}/?x={xval}&y=foo",
    "action" : "append",
    "template" : {
      "contentType" : "application/json",
      "fields" : {
        "user" : {"pattern" : "[a-z0-9_-]"},
        "xval" : {"type" : "number"},
        "firstName" : {}, "lastName" : {},
        "role" : {"required" : false}
      }
    }
  }]
}

Please note that “user” and “xval” fill-in values in the URI template, whereas “firstname”, “lastname” and “role” are going to be transmitted as JSON HTTP POST Body elements.

As noted earlier, h:link is a superset of h:ref, any h:ref can actually be represented as an h:link. Case in point: the earlier example of h:ref, when represented as h:link would look as follows:

{
  "h:head" {
    "title" : "Employees of North-East Department"
  },
  "department" : {
    "h:value" : "north-east",
    "h:label" : "North-East",
    "h:link" : [{"uri" : "http://api.example.com/departments/1234", "rel": ["about"]}]
  },
  "employees" : [

  ],
  "h:link" : [
    {"rel": ["self"],  "uri" : "http://api.example.com/users?dep=1234&page=4"},
    {"rel": ["next"], "uri"  : "http://api.example.com/users?dep=1234&page=5"},
    {"rel": ["prev"],  "uri" : "http://api.example.com/users?dep=1234&page=3"},
    {"rel": ["first"], "uri" : "http://api.example.com/users?dep=1234&page=1"},
    {"rel": ["last"],  "uri" : "http://api.example.com/users?dep=1234&page=10"}
  ]
}

As you can see, using h:link for simple references (single rel and a URI accessed via HTTP GET) is a bit more involved than just using h:ref, which is exactly why h:ref exists: to make easy cases easy. A lot of links are simple. The “next”, “prev”, “first”, “last” and “self” links, used for pagination being most common examples. We decided that for easy cases there should be an easy solution. That said, since h:link is a perfect superset of h:ref, you can write all your Hyper documents without ever using h:ref, using h:link instead, and they will still be perfectly valid.

h:value

It’s quite common that we have an object that may have several attributes, but one of the attributes is the “value” of the object. To solve for this common situation is why the core vocabulaty defines the the h:value standard property.

h:value Usage Example
{
  "product" : {
    "h:label" : "Box of Wine",
    "paid" : {
      "h:value" : "300",
      "currency" : "USD"
    }
  }
}

h:label

The purpose for the h:label property is best explain by this quote from the (Hypermedia Project Charter):

Some formats provide ways to give a human-readable label to an attribute. For example, where first_name is the attribute, “First Name” may be the label.

h:type

An array of type indicators. A type indicator provides semantic information for the object in context. It is very similar to the rel attribute of h:link, but h:link.rel indicates semantics of the linked element, whereas h:type indicates semantics of the object in the context.

Just like in the case of rel you SHOULD only use standard types as simple strings, all custom types should be indicated as CURIEd URIs. The order of these types should not be considered signficant.

h:pvt

Reserved namespace. Producers SHOULD NOT use a property named h:pvt and consumers MUST ignore h:pvt property even if present.

Name

There is actually no explicit property called “name” in Hyper, as you would see in some other hypermedia types. Rather, the key pointing to an object (from body, or another object) is used as the “name” when translating to/from the other media types.

{
  "h:head": {
    "version": "1.0",
    "title": "Department Employees",
    "curies": {"ex": "http://api.example.com/"}
  },
  "h:ref": {"self": "ex:users", "home": "ex:"},
  "h:link": [
    {
      "name": "search",
      "label": "Search",
      "rel": ["search", "collection"],
      "uri": "ex:search{?title}",
      "template": {"fields": {"title": {}}}
    }
  ],
  "department": {
    "h:value": "North-East",
    "h:label": "Department",
    "h:ref": {"ex:rels/department-link": "ex:departments/north-east"}
  },
  "employees": [
    {
      "employee-id": "cca78b82-59d5-49d3-bbfe-779de5248dbd",
      "firstname": "Brianne",
      "lastname": "Watsica",
      "job-title": "Senior Infrastructure Administrator",
      "h:ref": {
        "ex:employee": "ex:employees/cca78b82-59d5-49d3-bbfe-779de5248dbd"
      }
    },
    {
      "employee-id": "5e552416-f143-4bee-9a07-0e6fa8e96c12",
      "firstname": "Jose",
      "lastname": "Jakubowski",
      "title": "Regional Sales Representative",
      "h:ref": {
        "ex:employee": "ex:employees/5e552416-f143-4bee-9a07-0e6fa8e96c12"
      }
    }
  ],
  "budget": {"h:value": "500,000", "currency": "USD"}
}

You can see additional examples of how various Hypermedia types map to Hyper in the examples section.

Important HTTP Headers

While Hyper doesn’t mandate, expect or require usage of any specific HTTP Headers, some HTTP headers provide important capabilities for processing any hypermedia message. We left some important capabilities out of Hyper’s core vocabulary because they can be supported by said headers. Considering this, we believe it is justified to discuss them in this spec, for the sake of the completeness of the specification.

Profile

An HTTP header link [RFC6906] of profile [RFC6906] link rel type. The URI of the link SHOULD point to a resource that allows clients to learn about additional semantics (constraints, conventions, extensions) that are associated with Hyper document being processed. A great example of a such resource is an ALPS document.

Link: <http://alps.io/documents/search>; rel="profile",
      <https://api.example.com/alps/app-specific>; rel="profile"

Content-Language

The human language or languages of the intended audience for the enclosed content. See [RFC3282] for more information.


Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.