subscribe

Reinventing the wheel when encoding links in JSON

Links are critical when writing good REST APIs. Most APIs these days use JSON for their main format. Unfortunately, there’s no universally agreed on way to encode a link in JSON.

REST can really benefit from generic tooling. To write a generic tool that consumes links, it means that these tools might have to support a dozen formats or use other heuristics to find out if something is a link.

This begs the question, if there were a ‘good default’ serialization, what would that look like? Here’s my take.

The general shape

Most (but not everyone) of the API community has accepted ‘Web linking’ as the de-facto datamodel.

Broadly, this means that a link should at least have a URI (href), a relationship type (rel) and optionally the following attributes:

  • title
  • hreflang
  • type
  • media

Aside from support for these attributes, I feel that for something to be a successful ‘good default’ and actually adopted it would need a dedicated IETF RFC. One that’s ideally just focuses on links and not part of a larger standard.

What follows is a nonexhaustive list of existing link serializations in JSON.

HAL

{
  _links: {
    author: {
      href: 'https://evertpot.com/',
      title: 'My website!',
      hreflang: 'en-CA',
    }
  ]
}

HAL links are technically both a sub- and superset of RFC8288, because it adds features such as ‘templated’ links, but it also explicitly allows colon : in relationship types. HAL calls these ‘curies’ but they are not compatible with W3C CURIES, and also shouldn’t be used as such (they are not meant to be expanded, yes this is confusing).

Source

Collection+JSON

{
  collection: {
    links: [
      {
        rel: 'author',
        href: 'https://evertpot.com/',
        name: 'My website!',
      }
    ]
  }
}

Collection+JSON adds the prompt and render properties. It has no explicit support for type or hreflang.

Source

Siren

{
  links: [
    {
      rel: ['author'],
      href: 'https://evertpot.com/',
      title: 'My website!',
      type: 'text/html',
    }
  ]
}

Source

draft-wilde-linkset

{
  linkset: [
    {
      // Short
      author: 'https://evertpot.com/',

      // Long
      author: [
        {
          href: 'https://evertpot.com/',
          title: 'My website!',
          type: 'text/html',
          hreflang: 'en-CA',
        }
      ]
    }
  ]
}

Source

Web Thing API

{
  links: [
    {
      rel: 'author',
      href: 'https://evertpot.com/',
      mediaType: 'text/html',
    }
  ]
}

Source

JSON:API

{
  links: [
    {
      // Short
      author: 'https://evertpot.com/',

      // Long
      author: {
        href: 'https://evertpot.com/',
      }
    }
  ]
}

JSON:API has no standard way to encode title, type or hreflang and does not provide a way for a specific links relationship to appear twice.

Source

draft-nottingham-json-home

{
  api: {
    links: {
      author: 'https://evertpot.com/',
    }
  },
  resources: {
    author: {
      href: 'https://evertpot.com/',
      hints: {
        format: 'text/html',
      }
    }
  }
}

The json-home format has 2 places where link relationships are used, so I encoded an example for each.

There’s no way to encode title or hreflang, and no way for 2 resources to have the same link relationship.

Source

Many API formats use a convention as follows:

{
  author_url: 'https://evertpot.com/'
}

The Github v3 API uses this convention, and it’s formalized a bit more in RESTful JSON. You can also see this convention used in the OAuth2 discovery document, although it uses the _uri prefix.

There is also JSON-LD and Hydra, which is a pretty popular format. I’m not 100% sure if it’s possible to encode RFC8288 links in a lossless way.

The issue with not having a good default

New formats get invented every day, and many of them will have some need to encode a link.

Using something like JSON:API or Siren - although a good choice - is a big decision. Maybe even an emotional one. These standards are larger than their formats. They’re fairly opinionated and imply a buy-in into these opinions and their ecosystems.

The result is when authors of IETF RFC’s or other standards bodies run into this road block, they tend to just invent their own, often drawing inspiration on what’s out there.

I would really like to see a simple, uncontroversial IETF RFC that just makes a good suggestion on how to encode a RFC8288 link and a set of links so the next time the groups behind OAuth2, Web Things API or specialized single-purpose formats like json-home or Linkset only have to do something unique if they have an actual unique need.

How should it look like?

Based on what everyone is doing, it feels that the least controversial way to encode a single link would look a bit like this:

{
  rel: 'author',
  href: 'https://evertpot.com/',
  title: 'My website!',
  type: 'text/html',
}

And a collection of links would just be an array of these objects:

{
  links: [
    {
      rel: 'author',
      href: 'https://evertpot.com/',
      title: 'My website!',
      type: 'text/html',
    }
  ]
}

This allows links to appear in-line and and on a document-level, it can fully encode RFC8288 and it looks a lot many other things out there.

This is not really a specification, but I thought I would start with just floating this as an idea. One issue with this is illustrated well in XKCD 927, but my goal with this idea is not really coming up with a standard to unify them all. I just want something to exist for folks that don’t want to make this decision and I do think it needs the weight of a IETF RFC to make this happen.

What do you think?

  1. You can reply to this tweet to automatically see your response here.
  2. If you’re a dev, you can also send a pull request and edit in your comment in this article.

Bad Gateway is Toronto-based team of software engineers. We build good APIs and websites.

Want to work with me on your next project?

Email us

Web mentions