Making an API request even when data is in the store?

I’m using ember-data-1.0.0-beta.3. All works fine, but I see a network request every transition, even when I see data in the store using the chrome plugin.

App.ApplicationAdapter = DS.RESTAdapter.extend({
  host: 'http://api.myapp.dev'
});

App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});

App.UsersRoute = Em.Route.extend({
  model: function() {
      return this.store.findAll('user');
  },

  setupController: function(controller, model) {
    controller.set('model', model);
  }
});

findAll does a query, which queries for data from the API. If you want to get the data that’s already in the store, then you should filter. You can read more about filtering vs querying in the guide.

So the automatic caching is only for a single record? For example if I did

this.store.find('user', 1); 

it would check the store first?

find also makes a query call. I’m not sure, but I think that to get data from memory, you have explicitly ask for it by doing a filter. You’ll have to take into account the scenario where the item is not actually in memory.

You can use store.all('user') to get the already loaded data:

App.ApplicationRoute = Ember.Route.extend({
  model: function() {
    // load all user data once, when app load
    return this.find('user');
  }
})

App.ApplicationAdapter = DS.RESTAdapter.extend({
  host: 'http://api.myapp.dev'
});

App.ApplicationSerializer = DS.ActiveModelSerializer.extend({});

App.UsersRoute = Em.Route.extend({
  model: function() {
      // because the data is already loaded by ApplicationRoute, just get the record cache
      return this.store.all('user');
  },

  setupController: function(controller, model) {
    controller.set('model', model);
  }
});
2 Likes

Here’s the breakdown:

  • store.find - depends on how you call it, and depending on how you call it, it will do one of the following three things:

  • store.findAll - Always performs a server call. Also always returns the same RecordArray instance every time it is called.

  • store.findQuery - Always performs a server call. A different RecordArray (more specifically, an AdapterPopulatedRecordArray) will be returned on every call.

  • store.findById - Will only perform a server call if the record is not already loaded.

There is also store.filter, which will by default not perform a server call and only search for records that currently exist in the store. You can, however, tell filter to indeed perform a server call by supplying a query object, which will be delegated to a findQuery call under the hood.

Thank you. After reading this I just tried a probably very naive hack to use data from the store if it’s already loaded:

App.UserPhotosRoute = App.SecretRoute.extend
  model: ->
    user = @modelFor 'user'
    if @controllerFor('user.photos').get('loadedForUser') == user
      @get('store').filter 'attachment', (attachment) ->
        attachment.get('user') == user
    else
      @get('store').find('attachment', user_id: user.get('id'))
  afterModel: (photos, transition) ->
    user = @modelFor 'user'
    @controllerFor('user.photos').set 'loadedForUser', user

However, my issue is that attachment.get('user') returns a promise due to async loading. Is it possible to filter record arrays by async associations?

OK, so it turns out my naive hack was indeed very naive. Turns out something like this works much more smoothly:

App.UserPhotosRoute = App.SecretRoute.extend
  model: ->
    user = @modelFor 'user'
    if @controllerFor('user.photos').get('loadedForUser') == user
      @controllerFor('user.photos').get('model')
    else
      @get('store').find('attachment', user_id: user.get('id'))

However, I do still have a number of other areas in the app that are affected by an inability to filter based on associations with async: true set. Is there a “right” or “ember” way to handle such cases?