How Ember Data Loads Async Relationships: Part 1
Ember Data has two main ways to load asynchronous relationship data through adapters. It is not always obvious when or why Ember Data calls the adapter hooks that it does, or doesn’t.
In Part 1, we’ll explore how Ember Data responds to a few different common scenarios. In later posts we’ll look at some less-straight-forward examples.
Housekeeping
Before we get started, let’s talk about scope. All examples here will be in JSONAPI
. In most cases, this will translate pretty easily for users of RESTAdapter
or ActiveModelAdapter
.
Examples
For all of the following examples, we’ll be using the simple blog with posts and comments example. This is an easy relationship to use without having to explain any domain concepts.
The code for these examples can be found at github.com/amiel/ember-data-relationships-examples. Many examples are trimmed for brevity and the full source can be easily found by clicking on the filename.
Examples of JSONAPI
data are hard-coded in the adapters.
Here are the models we’ll be working with:
app/models/post.js
1 2 3 4 5 |
|
app/models/comment.js
1 2 3 4 |
|
Links
JSONAPI
allows for specifying that a relationship should be loaded via a url specified by the server.
Let’s see how this works with our first example blog post: Post #1.
Post #1 data
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
In this example, because the comments
key under relationships
matches the name of the comments
relationship defined in our post model, Ember Data will use the provided link to load data for that relationship. The default implementation adds any url prefix configuration to the provided url (such as the host) and fires off an ajax request to the provided link.
Therefore, accessing this relationship would cause the following ajax request:
1 2 |
|
However, it is possible to override this behavior by defining findHasMany
in the parent model’s adapter:
app/adapters/post.js
1 2 3 4 5 6 7 8 9 10 |
|
Using links is arguably the simplest way to load relationships with Ember Data if your server supports it. It is also an extremely useful adapter hook to get around some limitations in Ember Data, as we will see in a later post in this series.
Including relationship ids
It is also possible to include the ids for each object in a relationship. JSONAPI
calls this 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.
– http://jsonapi.org/format/#document-resource-object-linkage
Let’s see how this works with our next blog post example: Post #2.
Unlike Post #1, Post #2 has “data” instead of “links” in the “comments” section. Here’s what it looks like:
Post #2 data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Notice that each comment is a “Resource Identifier Object”, meaning that it has an id
and a type
, but no attributes
.
With this post, Ember Data will load each comment through the comments adapter by calling its findRecord
hook.
Therefore, accessing this relationship would cause the following ajax requests:
1 2 3 4 |
|
As before, this bahavior can also be configured, this time by overriding the findRecord
adapter hook in the comments adapter.
app/adapters/comment.js
1 2 3 4 5 6 7 8 9 |
|
Preloading the ids for relationships like this is nice when it makes sense for the interface to show placeholders for each item in the relationship and load details later.
Bonus Section
You might be wondering, is it a good idea to load each model of my hasMany
relationship in a separate ajax request?
In most cases, it is not a good idea. This is called the N+1 problem. Meaning, to load a blog post and its comments, we would need N+1 requests, where N is the number of comments on the post.
Fear not, as once again, Ember Data has your back. In order to turn N+1 requests into two requests, all you need to do is set coalesceFindRequests
. In this case, a different adapter hook will be called. Instead of findRecord
, findMany
will be called with an array of ids.
app/adapters/comment.js
1 2 3 4 5 |
|
This will, by default, cause the following ajax request:
1 2 |
|
No Links, No Ids
So, what if the post data doesn’t contain any relationship data, is it still possible to configure a way to load relationship data?
Let’s look at another blog post example: Post #3.
Post #3 data
1 2 3 4 5 6 7 8 |
|
The short answer is “no”, Ember Data has no facility for this. The long answer is “of course, anything is possible”, but it will take some hacks.
We’ll take a look at how deal with this situation later in this series.
Up next we’ll look at what happens when relationship data changes in Part 2.