JSONAPIAdapter - Inclusion of Related Resources


#1

Hi all,

I’ve been writing a serializer for my server component to serialize to / deserialize from JSONAPI documents and I want to incorporate the “?include” query parameter for inclusion of related resources.

What I’ve done to get this to work so far is this:

this.store.findRecord('first', 1, {
	adapterOptions: {
		include: ['seconds', 'seconds.thirds']
	}
});

And extended the JSONAPIAdapter like so:

buildURL(modelName, id, snapshot, requestType, query) {
	let url = this._super(...arguments);
	let include = Ember.get(snapshot, 'adapterOptions.include') || [];

	if (include.length === 0) {
		return url;
	}

	return url + '?include=' + include.join(',');
},

So firstly, adapterOptions is the only way I’ve found so far to get arguments from the findRecord method to the adapter so that it may append the includes to the URL. Does anyone know if this is the correct approach to take? When a snapshot is created, this property is set on it.

Secondly, if this is the correct approach, I’ve also had to override the findAll method on the JSONAPIAdapter to include the a 4th argument snapshotArray which is set by _findAll in “finder.js”. This is then passed into the buildURL method to access the adapterOptions (the current implementation passes null into the snapshot parameter).

findAll(store, type, sinceToken, snapshotArray) {
	let query;

	if (sinceToken) {
		query = { sinceToken };
	}

	let url = this.buildURL(type.modelName, null, snapshotArray, 'findAll');

	return this.ajax(url, 'GET', { data: query });
}

#2

Bump - I’d like to know if the above solution is the correct answer (the most elegant way of defining the included sideloads) as well.


#3

Just in case anyone is still following this, I am using this to specify includes for now until it’s implemented (place this in your application adapter):

buildURL(modelName, id, snapshot) {
	let url = this._super(...arguments);

	if (Ember.isNone(snapshot)) {
		return url;
	}

	let include = Ember.get(snapshot, 'adapterOptions.include') || [];

	if (include.length === 0) {
		return url;
	}

	let urlHasQueryParams = _.indexOf(url, '?') > -1;

	return `${url}${urlHasQueryParams ? '&' : '?'}include=${include.join(',')}`;
},

findAll(store, type, sinceToken, snapshotArray) {
	let query;

	if (sinceToken) {
		query = { sinceToken };
	}

	let url = this.buildURL(type.modelName, null, snapshotArray, 'findAll');

	return this.ajax(url, 'GET', { data: query });
}

#4

From what I can see here (https://github.com/emberjs/data/issues/3596#issuecomment-126604014), yes, this is the recommended way of handling this until EmberData adds a nice way to do it instead …

The one difference would be it’s recommended to override urlForFindRecord so you aren’t using quite as much of a battering-ram approach :wink:


#5

The only problem with overriding urlForFindRecord() is that, as far as I can tell, it wouldn’t apply to store.findAll().

Looking at the buildURL() method here https://github.com/emberjs/data/blob/v2.1.0/packages/ember-data/lib/adapters/build-url-mixin.js#L52, findAll() uses urlForFindAll() which doesn’t get given a snapshot, so it would not have access to the adapterOptions to get the include values.

So my method, while crude, should apply to any of the URL generation methods :stuck_out_tongue: