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 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.
application/vnd.hyper+json
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:
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.
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 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
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:
http://hyperjson.io/props/
, therefore the full URI form of h:ref
attribute is: http://hyperjson.io/props/ref
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.
Despite its flexible and extensible nature, Hyper is a very simple media type with a small vocabulary.
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:
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.
optional. Title of the document
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.
{
"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"}
}
}
Points to a JSON object where keys are link relationship types and values are corresponding URIs.
{
"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:
required. A URI [RFC7320] or URI Template [RFC6570], or a CURIE which expands into a URI or URI 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:
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”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:
label
: optional. Hint for human-friendly label to present to humans,
if an input form is rendered.required
: optional. Whether a value is required. Boolean true/false.
Default: true.type
: optional. One of: “text”, “number”, “date”, “hidden” or “boolean”.
Defaults to “text”.default
: optional. Default value.pattern
: optional. Restrictions on allowed values. Syntax must be the
same as used in pattern in HTML5Required. 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.
optional. String. Usually human-readable, for presentation hints.
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:
false
: do not embed.true
: embed using the best-effort approach. SHOULD use the content-type
headers returned by the resource to determine media type of the resource
being embedded.image/*
: embed as imageaudio/*
: embed as audiovideo/*
: embed as videotext/*
: embed as textOptional. 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:
append
: An unsafe, non-idempotent request to add a new item. (e.g HTTP POST)partial
: An unsafe, non-idempotent request to modify parts of an existing item. (e.g. HTTP PATCH)read
: A safe, idempotent request. [DEFAULT] (e.g. HTTP GET)remove
: An unsafe, idempotent request to delete an existing item. (e.g. HTTP DELETE)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.
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.
{
"product" : {
"h:label" : "Box of Wine",
"paid" : {
"h:value" : "300",
"currency" : "USD"
}
}
}
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.
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.
Reserved namespace. Producers SHOULD NOT use a property named h:pvt
and
consumers MUST ignore h:pvt
property even if present.
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.
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.
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"
The human language or languages of the intended audience for the enclosed content. See [RFC3282] for more information.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.