Hydra lite

For the past year or so I have been working with JSON-LD and Hydra. I have been an internal zealot for using JSON-LD and Hydra for building hypermedia services but I have been met with indifference for "simpler" formats such as HAL.

I think this preference for HAL is due to the cognitive baggage of RDF that comes with JSON-LD. For any developer being introduced to JSON-LD+Hydra, Linked Data and RDF is something they may have heard of in passing or never heard of. So for them, learning Hydra means having to learn all about vast world of RDF vocabularies, of which there are almost 500 indexed at Linked Open Vocabularies. That is a lot to research for someone who just wants to build their MVP microservice yesterday.

So what I want to do is introduce a usage of Hydra that is light on cognitive baggage. This light usage of Hydra doesn't introduce the entire universe of Linked Data for those that gravitate to HAL for it's "simplicity" and I'll show you why I put "simplicity" in quotes because JSON-LD is a much simpler and natural format for Hypermedia enabled JSON than HAL.

Rules of Hydra lite

These are the simple rules of Hydra lite:

  1. @context is optional
  2. Use duck typing for JSON only clients
  3. Use consistent JSON data types

@context is optional

The @context of a JSON-LD resource is great. I love using it to document my services. However to match the cognitive weight of HAL @context brings the entire world of Linked Data to hypermedia services that can be daunting for developers being introduced to Hydra.

Use duck typing for JSON only clients

Given that most, if not all, clients consuming these services will by JSON-LD naive, we have to enable duck typing allow those clients to detect linked resources (more on this below).

Resource

Let us start with what a resource is in hypermedia. A resource is simply an object with keys and values:

{
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html"
}

Links

With HAL, to link to another resource you use the special _links property

{
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "_links": {
      "self": "/entries/hydra-lite.json",
      "up": "/"
   }
}

If we want to provide additional data for the up resource, we have to move that to the _embedded field:

{
  "name": "JSON-LD/Hydra lite",
  "url": "http://eric.themoritzfamily.com/hydra-lite.html",
  "_link": {
    "self": "/entries/hydra-lite.json"
  },
  "_embedded": {
    "up": {
      "_links": {
        "self": "/"
      },
      "name": "Eric Moritz' Blog"
    }
  }
}

In Hydra lite this a lot simpler and JSON native:

{
   "@id": "/entries/hydra-lite.json",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {"@id": "/"}
}

And the "embedded" version:

{
   "@id": "/entries/hydra-lite.json",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {
      "@id": "/",
      "name": "Eric Moritz' blog"
   }
}

I think it is self evident how JSON-LD is simpler than HAL.

Linking and Duck Typing

While it is legal in JSON-LD to write the "up" link as {"up": "/"}:

{
   "@id": "/entries/hydra-lite.json",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": "/"
}

In my experience, most clients written to consume hypermedia are written in an ad-hoc way. While it would be ideal that every consumer of this service would use a JSON-LD enabled client, that is often not the case.

For JSON-LD naive clients, it is better to structure the link as I have above. The reason I use an object is for duck typing and future proofing the service.

If I would have started with {"up": "/"}, adding the name field to the resource would change the JSON type of the up property from a string to an object. Clients will likely break if they expect a string and get an object.

Resource typing

There are recurring patterns that I have seen in my work with services and the most common one has to be resource typing.

By definition, resource typing giving a resource a type field to classifies it for clients to identify its type.

This is often solved in an ad-hoc way like the following:

{
   "@id": "/entries/hydra-lite.json",
   "objectType": "BlogPost",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {
      "@id": "/",
      "name": "Eric Moritz' blog",
      "type": "Homepage"
   }
}

As often the case, different developers designed the schema of the BlogPost class and the Homepage class in isolation and didn't coordinate their field names.

JSON-LD has a standard field called @type to type resources:

{
   "@id": "/entries/hydra-lite.json",
   "@type": ["BlogPost"],
   "objectType": "BlogPost",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {
      "@id": "/",
      "name": "Eric Moritz' blog",
      "type": "Homepage",
      "@type": ["Homepage"]
   }
}

For backwards compatibility, we keep the "objectType" and "type" fields and bolt on the standard JSON-LD "@type" field.

You will notice that I used an array for the @type value. This is because @type can have multiple values.

For instance, we could provide a superclass for both these resources called a Thing:

{
   "@id": "/entries/hydra-lite.json",
   "@type": ["BlogPost", "Thing"],
   "objectType": "BlogPost",
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {
      "@id": "/",
      "name": "Eric Moritz' blog",
      "type": "Homepage",
      "@type": ["Homepage", "Thing"]
   }
}

Typing is extremely valuable for clients that need to render these resources. Generic templates/UI components can be designed for all Thing resources and more specific templates/UI components could be designed for BlogPost and Homepage resources.

Operations and Templated Links

Finally, I want to introduce Hydra operations and templated links. This is the only bit of Hydra that Hydra lite uses:

{
   "@id": "/entries/hydra-lite.json",
   "@type": ["BlogPost", "Thing"],
   "name": "JSON-LD/Hydra lite",
   "url": "http://eric.themoritzfamily.com/hydra-lite.html",
   "up": {
      "@id": "/",
      "name": "Eric Moritz' blog",
      "@type": ["Homepage", "Thing"]
   },
   "operation": [
      {"method": "DELETE"},
      {
        "method": "PUT",
        "expects": {
          "@id": "BlogPost",
          "supportedProperty": [
             {"@id": "name", "required": true},
             {"@id": "body"}
          ]
        }
       }
   ]
}

Operations allow you to declare which HTTP methods are available for a resource.

Templated links allow you to declare IRI templates for resources.

{
  "search": {
    "template": "/search{?q}",
    "mapping": [
       {
          "variable": "q",
          "required": true,
          "comment": "the search string to query on"
       }
    ]
  }
}

Conclusion

There is no argument from me that the inclusion of Linked Data principles to hypermedia services are extremely valuable for documentation purposes. However, when it comes to RESTful service developers looking to introduce hypermedia into their services, Hydra lite provides an easy way to bolt it on without a major refactor of the service and without weeks of research into what Linked Data is and what it provides.

While I am not a member of the Hydra W3C Community Group I welcome any questions and suggestions from the Hydra-CG team on how to improve Hydra lite.

Discuss on Hacker News

Eric Moritz
: linked-data / hydra / json-ld

Comments !