Force Ember Data to reload data from backend API

This is Ember Data’s default strategy for requesting data records (either findRecord or findAll):

What does this mean?

By default, this means our templates briefly display cached data (and eventually update the screen once the promise has resolved).

Sometimes this is not desirable. In a system where data is constantly changing, stale data can be lead to errors, customer frustration or money loss.

Wouldn’t it be nice to query, for every data request, the backend API for the freshest data?

It is possible!

Auction site

We have a list of auctions with their best bid. Our routes are /auctions and /auctions/:id. We want to ensure we have the freshest bid data at any time.

Typically auctions will be listed and users will click on a link to see an auction detail.

{{!-- app/templates/auctions.hbs --}}

<ul>
{{#each model as |auction|}}
<li>
  {{link-to auction.title 'auction' auction}},
  price: {{auction.price}}
</li>
{{/each}}
</ul>

Let’s ensure we reload the model every single time:

// app/routes/auction.js

export default Ember.Route.extend({

  model(params) {
    return this.store.findRecord('auction', params.id, { reload: true });
  }

});

That will do the trick! (We can add the same to findAll.)

There is a problem, though. As the link passes in the model directly, the model() hook above will not be called since the model is already known.

This can be solved by passing its id instead:

{{!-- app/templates/auctions.hbs --}}

<ul>
{{#each model as |auction|}}
<li>
  {{link-to auction.title 'auction' auction.id}},
  price: {{auction.price}}
</li>
{{/each}}
</ul>

Once routable components are out, model() will be called regardless whether a model or its id was supplied. This will transfer all caching decisions to model(), instead of being imposed by the framework.

Until then ONLY use ids in your links!

That’s how we force the store to always give us the freshest! If you’d like to have more control over when to reload… read on.

Advanced cache control

Our model hook snippet above showed us how to always reload in a findRecord request. That’s perfect for our current use-case, however, what if we need to reload (or backgroundReload) the record sometimes?

Overriding our adapter, we can customize four methods related to caching:

// app/adapters/application.js

export default DS.RESTAdapter.extend({

  shouldReloadRecord: function(store, snapshot) {
    return false;
  },

  shouldReloadAll: function(store, snapshot) {
    return false;
  },

  shouldBackgroundReloadRecord: function(store, snapshot) {
    return true;
  },

  shouldBackgroundReloadAll: function(store, snapshot) {
    return true;
  }

});

So we can make runtime decisions. For example, only background-reload data for mobile connections, unless it’s hot sale days. Or set records to expire after some time.

What’s more, we can create adapters specific to our models! For example, one at /app/adapters/auction.js will only affect Auction models.

For a complete picture of adapters and serializers, don’t miss the guide Fit Any Backend Into Ember with Custom Adapters & Serializers.

Additionally, the article Building an Ember Bookstore App discusses a similar approach to update prices.

The accompanying sample app, auctions, displays a list of auctions and links to their detail page. The backend is provided by Ember CLI Mirage. To simulate a busy backend, prices are updated every 3 seconds.

Code is up on Github! https://github.com/frank06/auctions

(For fun, try returning false from shouldBackgroundReloadRecord and pass { reload: false } to findRecord. You will notice that, upon clicking on an auction, there are no updates whatsoever.)

Any issues with reloading data? Is this article clear? Let me know in the comments!

Enjoyed this article? Don't miss my next one!

Leave me your e-mail for content that will help you master Ember:

Do you want to master Ember fast?

Leave me your e-mail for helpful updates delivered straight to your inbox.

(A few e-mails per month. No BS. Unsubscribe anytime!)