JSON Api Included Relationship Issue


#1

I’ve been using Ember to interact with a JSON Api project and seem to have run into an issue. I’m displaying related data that’s been included using a normal include query like this:

/staff-roster?include=region

The issue I’m running into is that since some of my relationships can be null, and since JSON Api only needs to include links, data, and/or meta, my responses with null relationships look like this.

"relationships": { "region": { "links": { "related": "/api/v1/staff-roster/21/region" "self": 
"/api/v1/staff-roster/21/relationships/region" } } }

Then when I access the relationship to be displayed in my table using staffRoster.region.label, Ember thinks the relationship hasn’t been loaded yet and sends a get request for every row in the table where region is null since no data element is included in the results. I’ve also tried switching over to a sync relationship with ‘region’: belongsTo(‘region’, { async: false }), but any row that contains a null region gives me this warning message in the console:

WARNING: You pushed a record of type 'staff-roster' with a relationship 'region' configured 
as 'async: false'. You've included a link but no primary data, this may be an error in your 
payload. EmberData will treat this relationship as known-to-be-empty.

It technically works when I use async: false since I’m including the related data, but I don’t like how it generates hundreds of those warnings as I grab each new page. Is there a better way to tell Ember to not fetch relationships that are null, or a proper way to handle async: false relationships where the relationship can be null so I don’t get that warning? Any help would be greatly appreciated.


#2

I think the problem is that in this context Ember doesn’t really know these are null relationships, or, put differently, they AREN’T null. When you provide a link you’re signaling to Ember “hey, this thing has a relationship, and you don’t have the data yet but you can get it here”. That’s the way Ember will interpret that payload. When you turn async to false on the relationship Ember is correctly concerned that you have given it a linked relationship but then told it that it’s not allowed to fetch that linked data itself.

I think the ideal solution would be actually sending a null relationship if the relationship is null. Otherwise Ember has no way to know which relationships are null and which ones aren’t.

If that’s not really an option then it kinda depends on how your data and API are structured and, but I’d say if the solution is making the relationship non-async then you should not be returning links for the relationship (you could always strip them out in the serializer if you wanted) and fetching the relationships that aren’t null manually. But anyway that depends a little more on your use case.


#3

It looks like the library I’m using returns links even if the data is null, so I’ll have to look into it more to see if there’s an easy way to configure that. As for fetching the relationships manually, since I’m already using include=region in my query, it includes related regions if they exist, so they should already be loaded, but I think I’d have to still find a way to configure the api to not return links for null relationships. If that isn’t feasible you’re saying I could just remove the link in the serializer if region_id is null?


#4

Yeah if the server returns links ONLY for null relationships on that model you could customize your serializer for that model to just remove any “relationships” that are links instead of payloads. That might be a little easier to customize than the backend depending on how easily configurable that behavior is on the backend.


#5

It looks like it returns links regardless of whether the relationship is null, it’s the data that isn’t returned on null relationships:

data: {type: "regions", id: "6"}
links: {self: "/api/v1/staff-roster/48/relationships/region", related: "/api/v1/staff-roster/48/region"}

links: {self: "/api/v1/staff-roster/87/relationships/region", related: "/api/v1/staff-roster/87/region"}

It looks like it would make more sense to try and configure the api to return data: null for relationships that don’t exist. I can look into customizing the serializer as a last result if the back end isn’t easily configured.


#6

I’ve run into this problem as well, it’s tough with async: false relationships that have links in their payloads.

For us, the fix was to write a serializer that removed relationships that didn’t have data as well as removing links for relationships with data. Since we keep all our relationships as async: false this works nicely.

Here’s our application serializer.

// app/serializers/application.js

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({

  normalize() {
    let hash = this._super(...arguments);

    if (hash.data && hash.data.relationships) {
      let relationshipIdentifiers = hash.data.relationships;
      let relationshipNames = Object.keys(relationshipIdentifiers);

      relationshipNames.forEach(relationshipName => {
        let relationshipIdentifier = relationshipIdentifiers[relationshipName];

        if (!relationshipIdentifier.data) {
          delete relationshipIdentifiers[relationshipName];
        } else if (relationshipIdentifier.links) {
          delete relationshipIdentifier.links;
        }
      });
    }

    return hash;
  }
});