Force Ember Data to reload data from backend API

Last reviewed in June 2019 with Ember Octane

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

  • If it is not loaded, load it
  • If it is already loaded, don't reload it. Instead, backgroundReload it

What does this mean?

  • reload queries for new data pre-emptively, that is, it waits until the promise with new data resolves
  • backgroundReload returns the local (cached) record, and then queries for new data

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>
    <LinkTo @route="auction" @model={{auction}}>
      {{auction.title}}
    </LinkTo>, price: {{auction.price}}
  </li>
{{/each}}
</ul>

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

// app/routes/auction.js

import Route from '@ember/routing/route';

export default class AuctionRoute extends Route {
  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>
    <LinkTo @route="auction" @model={{auction.id}}>
      {{auction.title}}
    </LinkTo>, price: {{auction.price}}
  </li>
{{/each}}
</ul>

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

import DS from 'ember-data';

export default class ApplicationAdapter extends DS.RESTAdapter {

  shouldReloadRecord(store, snapshot) {
    return false;
  }

  shouldReloadAll(store, snapshot) {
    return false;
  }

  shouldBackgroundReloadRecord(store, snapshot) {
    return true;
  }

  shouldBackgroundReloadAll(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 a Bookstore App with Ember Data discusses a similar approach to update prices.

The accompanying sample app, ember-reload-auctions-app, 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/ember-reload-auctions-app

(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? Join Snacks!

Snacks is the best of Ember Octane in a highly digestible monthly newsletter. (No spam. EVER.)