Description
This issue is a follow up to #887. If you haven't read that, please do so first.
I see #887 as an instance of a more general problem. Consider the following potential spec extensions:
- Accepting array values in
"data"
on requests, to support bulk operations (like the current bulk extension); - Accepting
null
in all"links"
values, to fix the contradiction identified in Spec contradiction:null
as link value #887; - Accepting an array for any value in
"links"
, not just newly-defined ones, to support RFC 5988 while having consistent link-processing rules; - Accepting an object keyed by type in
"included"
, as an alternative to an array, if we wanted to address the inconveniences some have raised about processing"included"
in static type systems; - etc.
Right now, none of these forms would be allowed. Moreover, we don't have the flexibility to extend the base spec in any of these directions, because doing so would break existing clients, which wouldn't expect values of the newly-legal type at the relevant key.
In general, our ability to add new document structures requires existing clients to be able to handle the new documents by interpreting them as (i.e. projecting them to) documents that they already know how to process. Right now, we have one projection rule—ignore unknown keys—that gives us all the extensibility we have.
In theory, this is enough for us to get any kind of extension we want. For instance, we could define "data2"
, "links2"
, and "included2"
keys that have the extended semantics discussed above. Then, clients that understand the new keys can use their values in place of the values in the old keys, while clients that don't understand the new keys will use the old keys' values (so the old values become a fallback).
The problem, maybe, is that "data2"
, "links2"
etc are really ugly, and having to mint a new key for every type of extension seems a pain. That got me thinking: perhaps it would be nice if we had defined some more powerful projection rules as part of v1. For instance, we might have had the following additional rules:
- When a message consumer encounters an unexpected array, it should replace it with the array's first item
- When a message consumer encounters an unexpected object, it should replace it with the value at the object's
"data"
key. (Note that this wouldn't play nicely with complex attributes as defined, but hold that aside for a sec.) - When a message consumer encounters a value of any unexpected primitive type, it should just ignore that value.
With these rules in place, the hypothetical extensions discussed above would look like so:
-
Allowing
null
on all links would become a non-breaking change (old clients would already know to just ignore such links; new clients could understand the positive "no such link exists" assertion). -
Ditto allowing all links to hold an array of items: links that are currently defined to only have one value could be represented consistently with arrays, and old clients would just take the first value, while new links could get arrays of values.
-
Representing
"included"
as an object keyed by type would likewise be a non-breaking change (though APIs that did so would have to duplicate a good deal of data—hopefully gzip would minimize this pain—and not use a type named"data"
). It would look like this:{ "included": { // unexpected object processed by old clients as included.data "type1": [], "type2": [], "data": [], // full, unsegmented included collection here } }
-
Using array values in
"data"
to create/update multiple resources would be interpreted backwards-compatibly by old servers as a request to only create/update the first resource. Servers that understand the request as a bulk operation could return something in their response payloads to signal this to clients; if this marker isn't present, the client would send subsequent requests to perform the remaining updates one at a time. As more servers moved to newer versions of JSON API, this fallback client logic would become less and less necessary, until it could be removed, at least from clients that know about the server they're dealing with.
So, considering all the above, I guess my question is: I'm assuming it's too late to add such projection rules to the base spec (right?), but how should these ideas manifest themselves in the extension system, if at all?
In the most extreme version, we could make supporting rules like this a prerequisite for using extensions. A less radical option would be to make an extension that itself signals support for rules like this (across the whole document, I guess). Finally, we might also want to not support changing the allowed values of base spec members at all, even with projection rules, because I imagine the statically-typed folks would find these rules a nightmare to deal with (though I don't know, really).
Anyway, these thoughts are pretty raw, so general reactions would be appreciated.
cc @json-api/owners