Belongs-to in microservices

I have two models, Post and Comment. They are stored in different databases and managed by different application servers.

I want to offer a way of searching Comments within a Post, so I add GET /api/comments?filter[post.id]=post12345&filter[text][match]=help.

As far as I can tell, that endpoint should include post relationships within each comment’s resource object if and only if it also includes the post in included and the user asked for that:

GET /api/comments?filter[post.id]=post12345&filter[text][match]=help&include=post
{
  data: [
    {
      type: "comment",
      id: "comment98765",
      attributes: {
        text: "some string that has 'help' in it"
      },
      relationships: {
        post: {
          data: {
            type: "post",
            id: "post12345"
          }
        }
      }
    }
  ],
  included: [
    {
      type: "post",
      id: "post12345",
      attributes: {…}
    }
  ]
}

But in my case, the comments service doesn’t have easy access to the post data, so it can’t fill in attributes. I could make a backend-to-backend call to look up that data and include it, but that might be prohibitively expensive and/or unnecessary.

If I use links, there’s no identifier that my client (ember-data) can use to match the Comments I get up to a Post.

See also ember-cli-mirage v0.4.0 release notes

It’s possible the premise of my question is wrong. The first example under Resource Objects is

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "/articles/1/relationships/author",
        "related": "/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
  }
}

It has relationships.author.data.id.

What that example doesn’t specify, though, is whether that author is fully described in the top-level included section and whether the client asked for GET ...?include=author.

The Compound Documents section says,

Compound documents require “full linkage”, meaning that every included resource MUST be identified by at least one resource identifier object in the same document

But it doesn’t talk about the converse: whether resources in the document may specify relationships for things that aren’t in the document.

In Clarification: non-compound documents and resource linkages, @michaelhibay says,

If you look in the spec examples themselves you’ll see the to-many example returns a link where the data fields are not resolved with an included section.

This further suggests that it’s OK to return relationships for things that aren’t included.

This further suggests that it’s OK to return relationships for things that aren’t included.

The default “included” parameterization is up to the developer to decide. If the consumer doesn’t include the parameter, you can easily just include a resource identifier object instead of the full resource. The goal is to include the required full linkage. If the consumer does send the “included” query parameter, you are required to return only the items listed in it.

In your case, it seems like you have a remote identifier for the post, so “type” and “id” or a resource identifier object would be all you are required to return in this case.

However, if this is a more generic application you could also look at Solr to build and maintain internal indices for your separate systems to make the full response faster.

I think this means putting { "type": "post", "id": "post12345" } in both data.[].relationships and in included. Is that correct? What’s the virtue of duplicating that? Why can’t data.[].relationships be sufficient?

I don’t understand this.

Perhaps my problem isn’t with JSON:API, but with ember-data. If I add { "type": "post", "id": "post12345" } to included then ember-data will consider that model loaded, but with an undefined title, body, author, etc.

It may be helpful for you to read the spec section on related resources again to understand the inclusion.

Note: A server may choose to expose a deeply nested relationship such as comments.author as a direct relationship with an alias such as comment-authors. This would allow a client to request /articles/1?include=comment-authors instead of /articles/1?include=comments.author. By abstracting the nested relationship with an alias, the server can still provide full linkage in compound documents without including potentially unwanted intermediate resources.

I don’t know ember’s support of this mechanism, but this seems like it would be the approach to solve your problem of needing full linkage at a high cost. Otherwise, this seems to be an issue with the way ember is handling the data, and it might be best to get help there.