Description
Hey look, our first erratum! 🎉
The contradiction
In the Links section, we say:
Each member of a links object is a "link". A link MUST be represented as either:
- a string containing the link's URL.
- an object ("link object") which can contain the following members...
But then, in the pagination section we say:
[Pagination keys] MUST either be omitted or have a
null
value to indicate that a particular link is unavailable.
This is a contradiction because the pagination keys are clearly members of the links object, so, by the first quote, null
shouldn't be an allowed value.
Goals for a solution
- (imo) The spec should continue to provide a mechanism for a server to affirmatively assert that a link (i.e. a resource with a particular relation) does not exist. This is a stronger statement than the server just omitting a link, which only tells the client that it might or might not exist. This mechanism should work with as many links as possible.
- Uniform processing rules. If some links need to be processed specially, I think that'll lead to pain and WTF moments for implementors/users.
- Backwards compatibility. This has two parts: 1) not violating any of our formal compatibility promises and 2) not breaking things in reality. Personally, I think we should focus more on whether we're breaking things in reality than in theory. (My reference for this is javascript, which, with the introduction of the
let
keyword and destructuring syntax actually changed the meaning of statements likelet[i] = [1];
from being an assignment to an array index to a destructuring assignment. This change was made because the spec committee scanned JS code online and found that it broke basically nothing in practice.) Then again, I also understand the importance of us keeping our formal commitments.
So, with those goals in mind...
Three solutions
No longer allow null
for any links; introduce an alternate "missing link" mechanism".
Here, I'm imagining something like this:
{
"links": {
"self": "http://example.com/people?page=1",
"next": "http://example.com/people?page=2"
},
"missing-links": ["prev"], //before, we'd have had "prev": null above
"data": []
}
Analysis:
- Offers a mechanism for indicating a missing link: yes.
- Creates uniform processing rules: yes.
- Satisfies backwards compatibility: sort of. I think it satisfies our formal commitments to backwards compatibility, because we can't be accused of breaking a behavior that was specified contradictorily in the first place. Such a behavior should be considered undefined, and now we're just defining it. However, while this satisfies our formal BC commitments imo, it might break some real world implementations that are already using null in their pagination links.
Allow null
for all links except the few where it's currently prohibited
Basically, we'd say something like:
... A link MUST be represented as either:
- a string containing the link's URL.
- an object ("link object")...
null
However,
null
MUST NOT be used inself
links,related
links at the top level or in relationship objects, orabout
links in error objects.
By disallowing null
as a value for self
, related
, etc. links, we wouldn't be contradicting the current spec, which says those links can only be a string or an object. But, we'd be allowing null
for everything else, thereby removing the contradiction with the pagination links.
Analysis:
- Offers a mechanism for indicating a missing link: almost but not quite; there are the few exceptions indicated above which wouldn't be allowed to be
null
. - Creates uniform processing rules: nope. We'd be allowing
null
for all but a few links which would, I imagine, lead to WTF moments among users and implementors who will wonder why there's this seemingly-arbitrary inconsistency that they have to learn about and remember. And it'll probably lead to some buggy implementations, as some people won't properly take it into account. - Satisfies backwards compatibility: yup, both in the formal and real world sense. (We have the flexibility to allow
null
for future links per this line.)
Allow null
for all links
This is exactly what it sounds like: we'd allow any link to be null
.
Analysis:
- Offers a mechanism for indicating a missing link: yup.
- Creates uniform processing rules: yup.
- Satisfies backwards compatibility: almost. It is clearly an additive change, in that we're expanding the set of valid documents, but I don't think it quite satisfies our formal commitment to backwards compatibility, because we're requiring that clients now parse documents (even just by ignoring the
null
value) that we didn't formally require them to handle before. However, this option does get a checkmark on the real-world compatibility front: I looked at all 16 client implementations that are listed on the JSON API site, and exactly one would be effected by this change (and then only in rare circumstances). (I'm happy to provide more details from this audit if y'all are curious.)
Thoughts?