The spec has two different ways of referring to the same concept; attributes in a document, and fields in a url parameter.
Could this be normalised into one name?
Quoting http://jsonapi.org/format/#document-resource-object-fields:
A resource object’s attributes and its relationships are collectively called its “fields”.
(Emphasis mine.)
@wimleers @ethanresnick While I realize the documentation seems to make this clear, you cannot fields a relationship, you can only sparse fields the data.attributes and includes.attributes, so clearly the documentation isn't as clear as it could be here?
you cannot fields a relationship, you can only sparse fields the data.attributes
@krainboltgreene I'm not sure I follow... you absolutely can effect relationships through the fields query parameter. E.g., the response might change like this:
GET /people/1?fields[people]=name
{
"type": "people",
"id": "1",
"attributes": { "name": "John Doe" },
"relationships": {}
}
GET /people/1?fields[people]=name,parent
{
"type": "people",
"id": "1",
"attributes": { "name": "John Doe" },
"relationships": { "parent": { "data": { "type": "people", "id": "4" } } }
You can see, with the relationship field included in ?fields, the relationship appears in the relationships object; otherwise, it doesn't.
Note that that a relationship field's contents are just the set of resource identifier objects (edges) pointing to the related resources, and not the related resources themselves. Is that the source of the confusion?
That's actually not what I meant and leads me to even more questions. I meant you can't do:
GET /people/1?fields[people]=type
and expect:
{
"type": "people",
"id": "1",
"relationships": { "parent": { "data": { "type": "people" } } }
But further, what spec suggests your first example would ever work? As far as I know a fields entry can't hide a relationship. The relationship still exists on the record, regardless of if the related record would have no attributes.
Now I'm a little confused. type and id aren't fields (see the definition @beauby cited above, and note how they're called out separately in the definition of a resource object). So type and id are always included, and can't be effected by the fields param.
Therefore, a request for GET /people/1?fields[people]=type wouldn't make sense — although exactly how the server should handle it is undefined. (That's another example of the undefined behavior you pointed out in #1305.)
What are you trying to do with that request?
But further, what spec suggests your first example would ever work? As far as I know a fields entry can't hide a relationship. The relationship still exists on the record, regardless of if the related record would have no attributes.
The fields parameter takes all resource objects included in a response and hides/suppresses some of their fields to satisfy the requirement that:
If a client requests a restricted set of fields for a given resource type, an endpoint MUST NOT include additional fields in resource objects of that type in its response.
In my example, the response is a single resource object of type "people". Therefore, ?fields[people] applies to limit which fields show up in that object. A relationship — again, the set of edges/resource identifier objects, not the related resources themselves — is a field just like any other. So, if the relationship name isn't specified in fields, that relationship gets suppressed.
As far as I know a fields entry can't hide a relationship.
Where is this impression coming from? Maybe we can clarify the spec somehow...
Haha, okay, lets hit this in two parts, with confusion 1 the "definition of attributes vs fields":
I know it wouldn't make sense, but I'm pointing out that because
A resource object’s attributes and its relationships are collectively called its “fields”.
and the sparse fieldset details
A client MAY request that an endpoint return only specific fields in the response on a per-type basis by including a fields[TYPE] parameter.
that perhaps the former definition is confusing since there's no example of anyone being able to alter how the relationships tree returns with a fields query key.
Confusion 2, or "hiding relationships via fields": You suggest that you can somehow hide a relationships entry with fields. There's no examples in the specification to suggest that, as far as I can tell and I'm pretty sure that's not how a majority of the server libraries work. Can you link me to any that might break me of this belief?
I guess in some way these two confusions are linked. I'm betting you're going to say that because a relationship tree is considered "fields" by that definition, you can sparse fields it to only show (or conversely not show) that relationship. That would not be my interpretation of the specification currently and I'm betting I'm not the only implementor who would have been confused by that.
So I just created an experimental application using jsonapi-resources, one of the larger ruby implementations on the server, and that absolutely works.
Looks like I wasn't the only confused person though:
perhaps the former definition is confusing since there's no example of anyone being able to alter how the relationships tree returns with a fields query key.
I'd love to add an example to the spec itself! Would you open a PR for that?
There is actually an example of this behavior on the examples page (see the request that has fields[articles]=title,body, which hides the author relationship), but I'd still love to have an example in the spec itself, since having the example on another page makes it too easy to miss.
I'm pretty sure that's not how a majority of the server libraries work. Can you link me to any that might break me of this belief?
I'm glad jsonapi-resources behaves per the spec, and I know my implementation does as well. I'm not totally surprised by the confusion, though. In general, JSON:API needs much more approachable docs and examples to supplement the spec, we just haven't had time.
If you know of any implementations that aren't implementing this behavior, please let me know, as I can open issues with them to get it fixed.
That's wild. I'm going to need to spend a hour updating my server side library. I would honestly love to just copy that example over to the documentation. I'll cook up a PR tonight.
I would like to add how I interpreted/implemented sparse fieldsets in my server-side framework. So my interpretation of fields was both attributes and relationships as a "union" in the same namespace, so when a client does a fields[type]=xxx,yyy only xxx and yyy should be present whether it was an attribute or relationship and all other "fields" suppressed. To me that is the specification intent because the purpose of "sparse fieldsets" was to empower the client to only GET exactly what "fields" then needed for their client-side requirements.
This feature in particular is important because it addresses this ubiquitous criticism that is leveled against REST API's of "over fetching/under fetching" you constantly read about. To be clear where "sparse fieldsets" addresses the "over fetching" and "includes" addresses the "under fetching". Especially when talking about "GraphQL" versus "JSON API".
With that said, there was one point in my implementation where it was unclear how to handle. If in the sparse fieldsets you are suppressing a relationship but that relationship has resource linkage to a related resource in the document. So my framework will preserve the relationship, but only the resource linkage and suppress the "links" and "meta" properties. My reasoning was without the resource linkage; including the related resource is useless because there is no resource linkage to it which defeats the whole purpose of the "included" resource to begin with.
With that said, there was one point in my implementation where it was unclear how to handle. If in the sparse fieldsets you are suppressing a relationship but that relationship has resource linkage to a related resource in the document. So my framework will preserve the relationship, but only the resource linkage and suppress the "links" and "meta" properties. My reasoning was without the resource linkage; including the related resource is useless because there is no resource linkage to it which defeats the whole purpose of the "included" resource to begin with.
This is definitely a strange case, and the best thing is probably for the client to correct its request to include the relationship in ?fields.
Technically, though, what the server's supposed to do is follow ?fields anyway and include the related resources without the relationship, even though that means the client won't be able to connect the included resources back to their source. This is specified when we say:
Compound documents require “full linkage”, meaning that every included resource MUST be identified by at least one resource identifier object in the same document...
The only exception to the full linkage requirement is when relationship fields that would otherwise contain linkage data are excluded via sparse fieldsets.
@ethanresnick Thanks for the clarification with sparse fieldsets suppressing a relationship that contains resource linkage. I do remember reading that note in the past and should have used that as guidance. I guess it felt odd to me to leave a "dangling" resource but technically you are right if the client specified in "fields" to suppress that relationship and also specified to "include" the suppressed relationship that is what the request "is" so it should have been suppressed with an "dangling" related resource in the document. The note does explicitly point that scenario out. I will have to adjust my framework accordingly...