Links with custom relations

In order to improve the hypermedia-ness of my API, I’ve wanted to add links with custom relation types to my resources. It wasn’t until recently that I realised this isn’t actually allowed by the spec:

Unless otherwise noted, objects defined by this specification MUST NOT contain any additional members. Client and server implementations MUST ignore members not recognized by this specification.

While I didn’t think much of this at first, investigating issues on Github has made me realise this does in fact apply to links objects too, so something like the following would be illegal:

{
  "links": {
    "avatar": "https://static.myapp.com/avatars/foo.png"
  }
}
  1. avatar is not a registered link relation type, of course, but more importantly,
  2. the spec does not define the links.avatar member, so it’s not allowed.

Reading through this issue I found a great clarification of why adding more links is important to me (emphasis mine):

Let me start with this part of the question: what is actually the core problem here. Discoverable actions, at least the way I suggest to implement them, do not pretend to solve the problem of building full hypermedia APIs, which need little to no documentation. It is assumed that the documentation on formats of specific requests is needed. The purpose of actions is to inform the client what is available for the given user on a given resource at a given moment of time – this is why actions should be returned dynamically, as part of a resource’s representation, and not via schema, docs, or in some other static way. So to me, the core problem is different from what you describe.

I’ve since combined two pieces of knowledge to decide on a course of action that will help me continue building useful features into my API for now, but also should remain spec-compliant as far as I can discern its future direction:

RFC8288 states that “extension relation types” can be used if they are URIs. URI syntax is defined in RFC3986, and here are some examples:

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment
      |   _____________________|__
     / \ /                        \
     urn:example:animal:ferret:nose

Colons are a necessary part of URIs.

Separately, JSON:API 1.1 extensions specify that extension members be namespaced using a :.

O happy circumstance! I shall proceed, for now, by defining an extension type for myself and using it as part of my custom link relation URIs. For example,

{
  "links": {
    "mycompany:avatar": "https://static.myapp.com/avatars/foo.png"
  }
}

I hope this isn’t too controversial, but I wanted to post it so that any fellow travelers following the same road I did will see what one weary API designer has done to reconcile the imperfections of messy APIs with the rectilinear world of specs.

I just read the start of your post, but I’m fairly sure you can place whichever links you want in a resource level links object: From here:

The optional links member within each resource object contains links related to the resource.

If present, this links object MAY contain a self link that identifies the resource represented by the resource object.

The way I read it, it does not say that the object may not contain any other links; it just specifies what the self link is.

We are frequently using custom resource links like your avatar.

That’s what I thought, but links is an “object defined by this specification”, and as such, it “MUST NOT contain any additional members”. Comments on the linked github issue, and other issues, seem to support this. I’m not too happy with it (it’s not very clearly communicated in the spec), but I believe that my “workaround” is both principled and convenient :).

I skimmed roughly through it, but didn’t find anything that said this - could you point me in the right direction?

Oh, it wasn’t in that github issue itself - it was in the comment here that led me to that issue! Links Object Clarification Needed

If we understand the spec correctly, this means “self” and “related” are
the only links we can specify.

Right now, yes, that’s correct.

I’m not sure if the spec has changed since then, but the issue linked to in that thread is still open. And there doesn’t seem to be any positive language in the current spec that suggests links objects are extendable with additional members. The spec does say this:

Note: Additional members may be specified for links objects and link objects in the future. It is also possible that the allowed values of additional members will be expanded (e.g. a collection link may support an array of values, whereas a self link does not).

This doesn’t suggest APIs are free to add their own links; but it does suggest you should take care if you do choose to do so, in case your custom links should conflict with future additions to the spec.

The 1.1 spec goes into much more detail about what “link objects” (distinct from the “links object”) may contain, but I still don’t see any language that counters the overarching

Unless otherwise noted, objects defined by this specification MUST NOT contain any additional members. Client and server implementations MUST ignore members not recognized by this specification.