Menu iconMenu

One decision you’re faced with when starting a new project is how you’re going to structure your API responses. If you’re using recent versions of Ember and Ember Data, then there’s a good chance you’re using the json-api specification as the basis for your api, since that works out of the box with Ember Data.

Ember Data  2.4 introduced a feature referred to as Reference Unification. The reference unification added two new methods to DS.Model instances:  belongsTo and hasMany , which should not be confused with the DS.belongsTo and DS.hasMany functions that are used to declare relations on your models.

These methods, combined with Ember Data’s adoption of json-api standard, give you some powerful tools to inspect and control related fields on your Ember Data models.

In this post, we’ll look at how some of the choices you make in your API design affect the behavior of your Ember Data models.

All of the code samples use an example set of models that look like the following models below. Specifically, we’re looking at the Book model and how you can manage the lifecycle of the related models: Author, and Chapters.

JSON API and loading relations

In our example, we want to be able to fetch a book and then fetch the related author and chapter resources.

There are two ways of defining relationships in the json-api spec. You can either provide the IDs of the related resources (the “data” method) or provide a url that the client can use to fetch the related resources (the “link” method).

Relations as “data”

The first way to return relations in your API is to return the actual “data” of the relationship. That just means you return the IDs of resources that related to the resource you requested. A json body using this “data” style looks like this:

Checking and loading “data” relations

The benefit of using this method is that it’s obvious who the author is and how many chapters there are, just by looking at the json response.  What that means for Ember is that you can inspect the IDs of your relations synchronously and without triggering any Ajax requests. Compare this to doing something like book.get(‘author.id’) which will trigger an Ajax request if the author has not been loaded yet.

Reloading “data” relations

Reloading related resources with the data method is pretty straightforward too (though it will seem a bit backwards once you get through the “links” way of doing things). To see if any relations have been added, removed, or changed, you simply reload the book model to get the new IDs.

Benefits:

– You always know if a related field has a value on the server without additional Ajax requests. There is no need to query the API for data if you know a relation is empty.

– Related models are referenced directly, so Ember Data is able to use the local store more efficiently by avoiding redundant Ajax requests.

– This is the only way to side-load relations.

Drawbacks:

– hasMany relations load each individual relation with a separate Ajax request. If you have a lot of related models, you’ll have a lot of requests fired off by your browser the first time you load those models (unless you side-load). That may cause significant performance issues.

– Reloading relations can be a bit unintuitive, because you reload the parent model instead of requesting a reload for a relation.

– You can’t reload just a single relation at a time.

– On the server, generating a list of related IDs for every API request may be prohibitively expensive and can slow down your API if not optimized properly.

Relations as “links”

Returning relations as links is the alternative to returning the IDs of the related objects.

A JSON payload using links would look like this:

Checking and loading “links” relations

The major difference from the data method is that you no longer know what the related objects are until you request them from the server. You’ll see that this has some benefits over the data method, depending on your requirements.

Reloading “links” relations

Reloading related resources when using the links attribute involves simply calling a reload on the related attribute. Ember Data has no way to cache a link reference, so every reload will trigger an Ajax request to fetch up to date data.

Benefits:

– Loading a relation will always trigger exactly one request instead of one request per related model like with the data method.

– Relations are not cached, so the related values will always be consistent with the server every time you reload.

– Reloading the parent model will not clear the relationships like the data method does.

– Allows for granular reloading of a single relation at a time

Drawbacks:

– You’re never able to tell the difference between “this book’s author has not been loaded yet” and “this book doesn’t have an author at all”. Compare this to the “data” method where the .id() method tells you if a relation is empty or just not loaded.

– You don’t know what you’re going to get until you make a request to the server. This may be a benefit if you don’t immediately care to know about a relation or if you never need that data.

– Side-loading data is not possible, because because the book model doesn’t know which of the related models are associated until a request is made.

Combining “links” and “data”

Ember Data will also work just fine if you combine the two strategies and return both link and data attributes. Returning both relationship attributes combines some of the benefits of each method while mitigating some of the drawbacks.  

A payload providing both pieces of information would look something like this:

When loading resources with both types of relationship references, Ember Data will use the “data” attribute for initial loads and the “links” attribute for reloading. I won’t include examples for the combined scenario, because they would be exact copies of the respective examples from the previous sections.

Conclusions

I think that Ember Data’s behaviour when provided with both “links” and “data” is a good indicator for the situations in which each of the two styles excel.

“Data” references are (in some cases) superior when loading data, because they allow you to side-load related resources and make use of the caching provided by the Ember Data store.

“Link” references are superior when reloading data, because all you need to do is make a single Ajax request to ensure that your client-side data is consistent with the server data.

A Day in the Life of an Ember Concurrency Task…

Media Suite's itinerant American developer explores one of the nuances of ember-concurrency.
Patrick Posted by Patrick on 19 October, 2016

Ember Blueprints: why they’re awesome

Brit-turned-Queenstowner and keen coder Jonathan on why he's a fan of Ember Blueprints. Note: This is part one of a. . . (yet-to-be-decided-how-many-parts) series on this subject.
Jonathan H Posted by Jonathan H on 11 August, 2017

Displaying Promises in EmberJS Templates

Brewer/developer/juggler Ewen Cumming on how to use promises in EmberJS templates.
Ewen Posted by Ewen on 19 October, 2016
Comments Comments
Add comment

Leave a Reply

Your email address will not be published.