Ember + JsonAPI - How to correctly handle relationships?

Hello I have a Post object which has zero-or-more Comment. My API looks as follows:

{
	"data": {
		"type": "post",
		"id": "1",
		"attributes": {
			"title": "Hello World"
		},
		"relationships": {
			"comment" : {
				"links": {
					"self": "/api/posts/1/relationships/comments",
					"related": "/api/posts/1/comments"
				},
				"data": [
					{ "type": "comment", "id": "1" },
					{ "type": "comment", "id": "2" }
				]
			}
		}
	}
}

My API URLs are as follows;

The following URL gets the posts: http://example.com/api/posts/

The following URL gets a post by it’s id: http://example.com/api/posts/1

The following URL gets all the comments of a particular post: http://example.com/api/posts/1/comments/

In Ember I have created my models as follows:

// in file /app/models/post.js
import DS from 'ember-data';
export default DS.Model.extend({
	title: DS.attr(),
	comment: DS.hasMany('comment')
});

// in file /app/models/comment.js
import DS from 'ember-data';
export default DS.Model.extend({
	text: DS.attr()
});

However when Ember tries to load the comments of a particular post rather then trying to load this using the related link in the comment relationships element, it goes and tries to load this from the following URL:

http://example.com/api/comments/

However my API does not provide that URL as a comment only exists as part of a post. I have searched on the internet and found some examples which say to override serializer for post. So I did as follows:

// in file /app/serializers/post.js
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
	normalizeResponse(store, primaryModelClass, payload, id, requestType) {
		if(id == null) return this._super(...arguments);
		delete payload.data.relationships['comment'].data;
		payload.data.relationships['comment'].links = {
			related: "/api/posts/" + payload.data.id + "/comments"
		};
		return this._super(...arguments);
	},
});

The above code works, but, on first load always gives an error because still Ember goes to look for the comments in /api/comments rather than /api/posts/:id/comments (I don’t understand why). However page still loads, and after this the error no longer pops up until I restart again.

However I wonder if this is the right way to do this.

Is there an out of the box way how to make Ember automatically use relationships link?

You can use an adapter for your model instead of a serializer. I have the same setup but with teams and members. I have a serializer that looks like :

import { computed, get } from '@ember/object';
import { inject as service } from '@ember/service';
import ENV from '../config/environment';
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';

export default JSONAPIAdapter.extend(DataAdapterMixin, {
  host: ENV.your_host,
  authorizer: 'your authorizer',

  namespace: computed('session.current_team', λ() {
  ⇚ `teams/${get(@, 'session.current_team')}`;
  }),

  session: service('session'),
  headers: computed('session.your_auth_header_value', λ() {
    ⇚ {
    'Auth-Header': get(@, 'session.your_auth_header_value')
    };
  })
});

And all my requests for members go to /teams/:id/members/(:id)

Ember Data will normally use the relationships link, but when data is also provided, Ember Data prefers the data.

In other words, I believe the following would also work to resolve your issue (I haven’t tried it though).

// in file /app/serializers/post.js
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
	normalizeResponse(store, primaryModelClass, payload, id, requestType) {
		if(id == null) return this._super(...arguments);
		delete payload.data.relationships['comment'].data;
		return this._super(...arguments);
	},
});

In case it is also useful, I wrote a bit about how Ember Data loads these asynchronous relationships: http://www.amielmartin.com/blog/2017/08/31/how-ember-data-loads-async-relationships/

In particular, check out Part 2, which specifically discusses this case: http://www.amielmartin.com/blog/2017/05/17/how-ember-data-loads-async-relationships-part-2/.

1 Like