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

17 November 2016

Definition

In data modelling, a symmetric relationship is a special kind of relationship
where the description of the relationship from the perspective of one end of
the relationship is identical to looking at it from the perspective of the other
end.

Friendship between people is a good example. If Megan is Selma’s friend, it
follows that Selma is Megan’s friend, too. On the other hand, the “knows”
relationship between two people is not a symmetric one. I might know Danny
Carey (the drummer of Tool), but that does not imply he knows me.

Historical background

My research into how to model and implement such a relationship in an Ember
application was sparked by this Stack Overflow question that was posed by
a reader of my book. It was more difficult than I thought it would be so I
was intrigued to find the (an) answer.

My solution turned out to have a fairly large API component, too, so the
following post will show both the server-side implementation (in Rails) and the
client-side one (in Ember).

If you don’t speak Rails, fear not. The code is straightforward and easy to
understand without any substantial Rails knowledge, thanks in most part to the gem that
makes it extremely easy to serialize data models and relationships to json:api
format, jsonapi-resources.

Data modelling

We’ll start with the data modelling part, which is the Rails side.

To be able to model our problem in the data layer, let’s say that Friendships
have a friender and a friended end of the relationship and a strength
attribute that measures how strong their friendship is.

We should create a (data) migration that will create a database table when run:

We select the friendships where either the friender or the friended is the
person we query it for. This is where the symmetric aspect of the relationship
is implemented. We don’t care if the person friended somebody or if that
somebody friended him, they are friends.

Note that modelling it this way, we could split up the symmetric relationship
into the two constituent parts. We could return only the friendships where the
person in question “initiated” it (was the friender), or “let himself be
friended” (was the friender).

Server endpoints, resources, serializing relationships

We could now turn our attention to setting up the endpoints and serializing the
model, and relationship data for the client application to consume. First, let’s
install the jsonapi-resources gem:

1

$ gem install jsonapi-resources

This gives us a jsonapi:resource generator that we can use to create both the
endpoints and the serializer for our resources.

This is rather standard Ember Data stuff, possibly with the exception of the
inverse definitions. Since we have two relationships between Person and
Friendship we need to specify the other end of each relationship and that’s
what we do with the inverse option.

With the models and routes in place, we can now see what the templates should look like.

There is nothing fancy going on here, either. The model is the person
retrieved in the route. For each friendship that he has, the friender’s and
the friended’s name are rendered along with the strength of the relationship.
(Either friender or friended will be the person itself, but we can ignore that
in the first version.)

This naive approach works, the friendships for the selected person are listed correctly:

A 2N+1 problem

However, looking at the requests to the backend for just one page, one gets the
impression that we’re not done yet:

For each friendship the person has, two requests are sent to the backend. One to
fetch the friender and another one to fetch the friended person. This is not
an N+1 query problem, this is worse, a 2N+1 query problem!

On top of that, those requests are sent for no good reason as we’d previously
loaded the people referred by those friended and friended relationships.

In the next part, we’ll see how these wasteful requests can be eliminated and
we’ll also make the person details page less perplexing by not displaying the
person whose page we’re looking at in the relationships. Stay tuned!