Relationships.relname.data require included member?

If my response has relationships with an array of relationship identifiers is there an expectation that those same identifiers should be found in the included member? That seems to me to be the only way a {“type”: “foo”,“id”: “abc”} relationship data member could resolve to a resource without making an assumption about how resource URLs are formed? In my first (ten) readings of the spec it seems like I can have a relationships.relname.data member without an included member but that seems wrong:

links:                                                                                                                                        
  self: /widgets/abc-123                                                                                                                      
data:                                                                                                                                         
  type: widgets                                                                                                                               
  id: "abc-123"                                                                                                                               
  attributes:                                                                                                                                 
    name: "can opener"                                                                                                                        
    qty: 47                                                                                                                                   
  relationships:                                                                                                                              
    location:                                                                                                                                 
      data:                                                                                                                                   
        - type: locations                                                                                                                     
          id: "1234"                                                                                                                          
        - type: locations                                                                                                                     
          id: "1235"                                                                                                                          
      links:                                                                                                                                  
        self: /widgets/abc-123/relationships/location

Do I need to make sure there’s also an included member as a means of resolving those resource identifier objects?

included:
  -  type: locations
     id: "1234"
     attributes:
       warehouse: Briarcliff Manor
       aisle: "45"
       shelf: "33"
       bin: "1"
    links:
       self: /locations/1234
  -  type: locations
     id: "1235"
     attributes:
       warehouse: NYC
       aisle: "4"
       shelf: "3"
       bin: "11"
    links:
       self: /locations/1235

Thanks in advance.

I’d make a few observations here.

  • You do have a URL that resolves those locations… the “self” link of the location relationship (self: /widgets/abc-123/relationships/location)
  • Consider renaming that location relationship to “locations” (self: /widgets/abc-123/relationships/locations)

That said, I assume what you’d prefer is a link to each of the specific locations in the array. The reason I would want that is because I’d want to minimize the number of round-trips to the server. Do you have a different reason? If not, then making those location resources available as included resources is fine IMO. In our API we have a notion of “default includes”, which we define in the controller. These are resources that show up as included resources regardless of whether the client specifies them in the “includes” query parameter.

@senorbacon Thanks for the feedback. I’ve read further and see that I MUST return the relationships data resource identifiers in included[] (unless the include query parameter limits that).

The self link I was referring to was for the relationships.location.data[] identifiers which consist only of {type,id}. From this I must somehow infer a way to get to the {type,id} object. The practice seems to be to have a top-level resource with the same name as the type and I guess it’s up to the implementor since the included[] array is required and it’s up to me what links.self URL I put there.

Regarding singular vs. plural for the relationships, that’s something I was scratching my head about as the examples show singular and plural, perhaps for the to-one vs. to-many relationship types? The example given shows singular author and plural comments. So now it is more clear to me. It might help for the specification and or recommendations to more clearly state the plural vs. singular guidelines.

Thanks!

Where do you see that you MUST return relationships data in the included part of the response? My reading of the spec says that it’s optional to return those by default, and that you can allow the client to request includes:

An endpoint MAY return resources related to the primary data by default.
An endpoint MAY also support an include request parameter to allow the client to customize which related resources should be returned.

It’s up to the API author to determine the URL scheme for a given resource type. Top-level resources aren’t always appropriate. For instance, a collection of tags applied to an article might better be modeled in the URL as /article/:id/tags, rather than /tags?filters[article]=:id

Given that, the point of using links is so that clients do not have to infer the URL.

Including a the representation is up to you as the implementer, there is no requirement. However, the question shows you aren’t fully understanding the links object, as this solves the problem of URL building you have created.

"relationships": {
    "author": {
      "links": {
        "self": "/articles/1/relationships/author",
        "related": "/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
}

As you see above, the data element is only part of the necessary representation for each relationship in your case. Each one of those full representations would be the items in the array, and there would be no issue.

‘Resource Identifier Objects’ are mostly used for full linkage in compound documents within the included section to reduce the payload size or complexity of the links between related resources. If you had multiple links in between, each step you have the option to create a deeply linked relationship and not send the linking ‘resource identifier objects’. See the note here starting with “Note: A server may…” for more detailed context.

To your first point, be careful to make sure people understand the format of your URL is a convention stemming from Ember.js and not a requirement of the specification. The spec only requires there is a resource URL which identifies operations on the relationships, which doesn’t need to be there and can be discovered as a link itself.

To your second point, I think you have exposed an error in your implementation. Default includes do not show up regardless of a clients submission of the includes query parameter, they are in fact mutually exclusive. If a client submits an includes parameter, your service is to include only those items along with intermediary steps required for full linkage. The includes parameter MUST completely override the default behavior of your service.

Don’t waste any brain cycles on this, it’s a completely unimportant discussion. A service should not be projecting its contract through the URLs of its resources in any way, ever. Ideally the URL should be completely opaque to the consumer, but even if they aren’t they should simply be a projection or realization of the contract definition which does set the contract. It should work just as well if the actual resolvable URL was .../relationships/location, .../relationships/locations, or .../asdfasdasd/ajfikadfajsdkj.

Here it says:

Compound Documents

To reduce the number of HTTP requests, servers MAY allow responses that include related resources along with the requested primary resources. Such responses are called “compound documents”.

In a compound document, all included resources MUST be represented as an array of resource objects in a top-level included member.

Compound documents require “full linkage”, meaning that every included resource MUST be identified by at least one resource identifier object in the same document. These resource identifier objects could either be primary data or represent resource linkage contained within primary or included resources.

The only exception to the full linkage requirement is when relationship fields that would otherwise contain linkage data are excluded via sparse fieldsets.

So I see that I MAY have a compound, not MUST. Sorry. But if I do have a compound document response then I MUST have all the included[].

You misunderstood my statement regarding includes[] vs. the includes= queryParameter. I understand that the QP is used to limit what’s in includes[].

Further to my earlier question regarding relationships data, there is no way for a client to know how to find out more about the relationship data unless the server provides the includes[] that links the type+id to the resource data (and optionally its self URL).

Yes, you’re right, thank you for pointing that out.

1 Like

Just to be clear, the spec is saying that if you have included resources (a compound doc), those resources MUST be referenced in the relationships array of a parent resource, not that you MUST include full-bodied resources in the included array if there’s references to them in the relationships array of the parent resource.

IOW there should be no resource in the included array that aren’t linked by anything else in the response.