Two-way symmetric relationships in Ember with JSON API - Part 2

29 November 2016

In Part 1 of this series, we saw what symmetric relationships are, how to
model them on the back-end (using Rails) and how to implement a simple app that
displays such relationships in Ember.js, adhering to the JSON API specification
for serializing data.

The solution we came up with worked but was a very naive one. For N relationships
a resource had, it made 2N+1 queries to the back-end to display them. We should
do better and in this post we’ll see how.

(If you haven’t read the previous post, this one will not make much sense,
so I recommend to at least skim it before you delve into this one.)

One request to rule them all

We’d observed that for each person whose friendships we wanted to observe, our
application made 2N+1 requests. One to grab the friendships themselves, and then
two requests for each friendship in that collection, one to fetch the
friender, one to fetch the friended person.

We also noticed that those people (instances of the Person model) had already
been fetched at that point so the extra requests were for nothing. Our mission
is thus to reduce the 2N+1 requests to a single one, the one that fetches the
friendships.

How would Ember (Data) know?

If we take a look at the data returned by the request for friendships, you can
see that both the friended and friender end of each are just links with no
type or identity information (like /friendships/1/friended). This is all
that’s needed for an asynchronous relationship, since the client (Ember Data, in
this case) can just fetch the relationship data when/if it needs it.

The solution, then, might be to include some data about the resource that the
relationship refers to. In the JSON API vocabulary this is called resource
linkage:

Resource linkage in a compound document allows a client to link together all
of the included resource objects without having to GET any URLs via links.

Digging around in the jsonapi-resources source, we find a relationship
option called always_include_linkage_data that seems to do what we need. Let’s
add that to the relationships of the friendship resource and see:

If we now reload our Ember app, we see how a data key was added to each
relationship in the response, uniquely identifying the person resource that is
the friender (or friended) in the friendship relationship:

Furthermore, the extra XHRs we wanted to eliminate are now indeed gone as Ember
Data is smart enough to just use the referred resources that are already in the
store:

Let’s just be friends (not friendeds or frienders)

We have now achieved what we’d wanted and only have to make one request per
person to fetch and display their friendships.

It looks a bit weird, though, that when a person’s friendships are displayed, we
also display the person’s name, too:

Let’s fix that by transforming the friendships of the person to an array where
each item only contains the friend’s name (and the strength of the friendship):

Nothing fancy going on, we check which end of the relationship the person in
question (the model) is, and then only return the other end.

We should now use friendships in the template instead of model.friendships:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<divclass="panel panel-default"><divclass="panel-heading"><h3class="panel-title">Friends of {{model.name}}</h3></div><divclass="panel-body">{{#iffriendships.length}}<ulclass="friend-list">{{#eachfriendshipsas|friendship|}}<liclass="friend-list-item"><spanclass="name">{{friendship.friend.name}}</span><spanclass="badge">{{friendship.strength}}</span></li>{{/each}}</ul>{{else}}<divclass="empty-list"><pclass="empty-message">{{model.name}} has no friends :(</p></div>{{/if}}</div></div>

It works, we indeed only see the friend’s name, not the person’s:

Resources

Hopefully you can now implement a symmetric relationship with relative
ease, the next time you encounter it.