Clarification on the sparse fieldset response


#1

The spec states that If a client requests a restricted set of fields, an endpoint MUST NOT include additional fields in the response.

The definition of fields is A resource object’s attributes and its relationships are collectively called its “fields”.

Does this mean that if the request does not explicitly ask for attributes from the main resource its attributes will be returned null?

For example, a request for a persons comments:


GET /people/9?fields[comments]=body HTTP/1.1
Accept: application/vnd.api+json

 {
  "data": [
    {
      "type": "people,
      "id": "9",
      "attributes": null,  /* because none were requested */
      "relationships": {
        "comments": {
          "links": { /* ... */ },
          "data": [
            {"type": "comments", "id": "6"},
            {"type": "comments", "id": "12"}
          ]}
        /* No other relationships are allowed because they weren't mentioned */
       },
       "links": {
       "self": "http://example.com/people/9"
      }
    }
  ],
  "included": [
    {"type": "comments", 
      "id": "6",
      "attributes": {
         "body": "OMG! Who cares!"
      }
    }, {
      "type": "comments", 
      "id": "12",
      "attributes": {
        "body": "I like XML better"
      }
   }
  ],
/* ... */
}  

#2

The fields parameter is meant to work on a per-type basis, so adding fields[comments]=body to the URI is supposed to mean the client has “requested a restricted set of fields” for type: "comments" resources, but not for people resources. To narrow people fields, a fields[people] parameter would also have to be provided. Since it wasn’t, all people fields could be included in your example response.

It sounds like maybe the sentence that you quoted:

If a client requests a restricted set of fields, an endpoint MUST NOT include additional fields in the response.

should instead read

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.

So, to produce the response you showed in your example, the URI would be:

/people/9?fields[people]=comments&fields[comments]=body&include=comments

A few notes about the above:

  • The include parameter isn’t strictly necessary, since the server is allowed to include some relationships by default (assuming the user doesn’t provide their own inlcude).

  • The fields[people]=comments part is needed to say that “people resources should only contain the comments field”, as in your example. But including fields[comments]=body doesn’t effect at all the fields included under people resources. So, for example, if your fields restrictions were: ?fields[people]=&fields[comments]=body (note the empty string value for fields[people], which means “give me no fields”), your response would look like:

{
  // links, etc
  "data": {
     "type": "id",
     "people": 9,
     // the attributes and relationship keys could 
     // be omitted below, but they couldn't be null,
     // because the spec says, for consistency, that, 
     // if they're present, they must be objects
     "attributes": {},
     
     // no comments relationship here
     "relationships": {}
   },
  "included": [{
    "type": "comments",
    "id": "1",
    "attributes": { "body": "yadayada"},
    "relationships": {}
  }]
}

Does that make sense?


#3

I understand now and changing that sentence to what you suggested should prevent that confusion in the future.

Now where you said:

Does this mean that unless the relationship is returned by default it must be specified using the include parameter before fields parameter can be used on that relationship?

../people/9?fields[comments]=body  //Incorrect because the comments relationship is not included by default
../people/9?include=comments&fields[comments]=body  //Correct

Is there a reason the API can’t just assume the relationship is “included” if a valid field set is requested, the relationship type is included in the request?

../people/9?fields[comments]=body //Correct because comments.body is a valid field set of people
../people/9?fields[publisher]=address //Incorrect because publisher is not related to the people resource

#4

Correct me if I’m misunderstanding, but it sounds like the confusion might be around this: that field restrictions apply to resources of a given type (as in the type) key, whereas the include parameter operates on relationship paths (i.e. key names in the "relationships" object).

And, crucially, the same resource can have multiple relationships that point to resources of the same type. For instance, an article resource could have an author relationship and an editor relationship, both of which point to type people resources.

So, /articles/9?include=author&fields[people]=name would put the author resource in the "included" collection (the article would be the primary data), but that included author—because it is a people resource—would only come in with its name attribute.

Therefore…

Would be a perfectly valid request, but if the related comments aren’t included, there are simply no resources of comments type in the response, so the parameter has no effect.

Does that make sense? And if so, it’d (again) be awesome if you could point us to the parts of the spec that caused confusion, so we can fix them.


#5

That makes total sense and the confusion was wholly on our side.
In our implementation all of our relationships are related to a given type and we didn’t consider that they don’t have to be.

Thank you, you have helped us immensely.


#6

One point that isn’t clear in the spec is what to to if the type doesn’t exist.

For example it’s defined for include: “If a server is unable to identify a relationship path or does not support
inclusion of resources from a path, it MUST respond with 400 Bad Request.”

But it’s not defined what to do for invalid types in “fields” (and the same is missing for unsupported “page”, “sort” and “filter” values or if the server doesn’t support one of theses features).


#7

Assuming the response document in the original question and the following

Does this mean that as soon as you explicitly request a restricted set of fields using fields[type] you must include the relationships for this type in the fields list, otherwise they will be excluded even if they are listed in the include parameter?

In other words if type people had first_name and last_name as attributes and comments as a relationship, then omitting the fields[people] request parameter would automatically mean that it is interpreted as being fields[people]=first_name,last_name,comments?

So adding fields[people]=first_name,last_name,comments to the URI or leaving it off has the same result?

Thanks


Sparse fieldsets and relationships
#8

l[quote=“Ziege, post:6, topic:221”]
…what to do for invalid types in “fields” (and the same is missing for unsupported “page”, “sort” and “filter” values or if the server doesn’t support one of theses features).
[/quote]

The server should return a 400 Bad Request because it is a client error to request an operation that isn’t supported. Hopefully, the server also returns an error member that includes helpful specifics of what was wrong.


#9

Yes, that’s how I implemented this. But I wonder why the spec sometimes explicitly defines what the server has to respond and sometimes not. IMHO this could be improved.


#10

@ziege Can you open an issue for this on the main repo? I agree that it’s not defined, and that it probably should be addressed, but I’m not immediately sure what the right answer is or what we can specify backwards-compatibly.


#11

Yes, exactly.

Almost. If you explicitly request a specific set of fields but don’t include one of ?included relationships in that field list, the related resources are still included but the resource identifier objects pointing to them are not.

So, for example, the response to /people/1?fields[people]=first-name&include=comments would look like this:

GET /people/1?fields[people]=first-name&include=comments
{
  "data": {
    "type": "people",
    "id": "1",
    "attributes": {
      "first-name": "John"
    },
    // an empty relationships object because 
    // no relationships were listed in fields
    "relationships": {} 
  },
  "included": [{
     // all of people 1's comments
  }]
}

Cases like the above are explicitly called out in the specification as being an exception to the “full linkage requirement” (i.e. the requirement that a client must be able to connect resources from "included" to the resources that resulted in their inclusion). Here’s the relevant paragraph:

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.

So, making a request that ?includes a relationship while excluding it from ?fields is allowed, and has a defined behavior, but it’s probably a bad practice, since it makes it harder for the client to connect the included resources to the primary data in some cases.


#12

Thank you so much for this detailed explanation! This is very helpful.

What do you think of something similar to the language agnostic test suite that the json-schema project has for json-api?

I know there is a json-schema for json-api response docs, but I don’t think a case like this would be covered.
I find myself spending a lot of time arguing over details of the spec when trying to implement it.

A set of input data and sample queries with their results would be extremely helpful. This way library maintainers could implement against a common test suite.

I think this issue on the repo is asking something similar.

Thanks again!!
Much appreciated

Henning


#13

@ethanresnick: I just opened an issue and listed 16 cases in which it’s unclear how the server should process the request: https://github.com/json-api/json-api/issues/939


#14

Yes, I would definitely like to see this and, as you pointed out, it’s been discussed in other issues. I think the only issue at present is that none of the editors have the time to put such a test suite together (or even think in much depth about how it should work). I’m currently working on extensions (as @ziege can tell you, I’m already behind on that :smile:) and the other editors have various other commitments.

For now, though, we’re happy to answer any implementation questions in here as they arise.

Also, if you’re up for trying to tackle the test suite yourself, that would be immensely valuable and appreciated, and we’d do our best to help!

Thanks Christoph! That issue looks awesome + very thorough! I’ll try to chime in soon, and hopefully the other editors can too.