-
Notifications
You must be signed in to change notification settings - Fork 890
Description
Right now, the JSON:API spec uses the terms "query parameter" and "query parameter name" in a way that's inconsistent with other web standards, and internally inconsistent throughout the spec.
Consider the URL /articles?page[cursor]=xyz&page[size]=10
. Does that URL have one query parameter (named page
) or two (one named page[cursor]
and one named page[size]
)?
From a standards POV, the best/only answer comes from the WHATWG Url standard, which says (see step 4) that it has two. Accordingly, the following code logs first "page[cursor]"
and then"page[size]"
:
for(let [k,v] of new URLSearchParams("?page[cursor]=xyz&page[size]=10")) {
console.log(k);
}
(Other standards, like RFC 3986 and the HTTP RFCs don't have a concept of distinct query parameters at all; they only have the idea of a single query string.)
When the JSON:API spec says, though, that: "The page
query parameter is reserved for pagination", we're clearly intending to cover the whole set of page[offset]
, page[cursor]
, page[xxx][yy]
parameters with that statement — not just the literal parameter page
.
Therefore, I propose that we explicitly define the concept of a "query parameter family", distinct from an individual query parameter. That will let us use the term "query parameter" in a way that's consisten with WHATWG and, perhaps more importantly, it'll clear up a few spec ambiguities:
-
In error objects, we can clarify that the
"parameter"
key is supposed to refer to the specific parameter, and not the parameter family's name. For example:The request
GET /xxx?fields[books]=tilte&fields[people]=name
has a typo: thetitle
field has been misspelled astilte
. In the resulting error object, I think we want something like:{ /* ... */ "source": { "parameter": "fields[books]" } }
Having the parameter be
fields[books]
is more useful than having it just befields
but, at the moment, it's deeply ambiguous which is correct. In the sparse fieldsets section of the spec, for example, we manage to refer to both "afields[TYPE]
parameter" (which impliesfield[books]
could be the source) and "thefields
parameter" (which seems to imply the opposite). -
When we say that "Implementation specific query parameters MUST adhere to the same constraints as member names", I don't think we're intending to disallow
customParam[xxx]
as a parameter name — even though it contains square brackets, which are not valid in member names. We can make this clearer by saying that implementation-specific query parameter names must come from a family whose name adheres to the member name constraints. This is similar to the point above about us actually intending to reserve the entirepage
family for pagination, and not just the literal page parameter. -
Finally, when we specify how the server can infer the value of the
profile
query parameter, we say that "a server MAY define an internal mapping from query parameter names to profile URIs". In this usage, I think we actually want to give the server the choice about whether to map a whole family of parameter names to a given profile, or whether to just map concrete parameters. For example, a server might want to map the presencepage[offset]
query parameter to one profile, and apage[cursor]
parameter to another, so that both pagination schemes can be supported (without need for theprofile
parameter). By contrast, for a filtering profile, the server might want to map all parameters in thefilter
family to that profile, to support (e.g.) arbitrary operators that might go in the concrete parameter name (?filter[myCustomOperator]
). Having a distinction between a parameter and a parameter family would let us specify this as well.
So, with those motivating use cases, here are the definitions I'd propose:
-
A query parameter is a
(name, value)
pair that's part of a URL's query string. The name of a query parameter is serialized to and parsed from the URL per the process in WHATWG URL. (Under this second sentence, the URL?fields[books]=x&fields[people]=y
has two query parameters.) -
A query parameter name family is a set of query parameter names derived from a base name. The base name must be a valid JSON:API member name. The family includes all query parameter names that start with the base name and are followed by zero or more instances of the square-bracket-surrounded legal member names. In ABNF:
query-name-family-member = base-name *( "[" *legal-member-name "]" )