How to handle Meteor's optimistic login system


#1

I think the Meteor.Accounts attempts to log in “optimistically” or causes some kind of “race condition” such that it is trying to fetch data before the data is completely available. This is most apparent when completely reloading the application, it appears the user is briefly logging in.

For example, on my user collection, I have a recent key. When I go to /products, it should use this recent tag on the route.

Router.configure({
  waitOn: function () {
    return Meteor.subscribe('currentUser');
  }
});

Router.map(function() {
  this.route('Products.all', {
    path: '/products',
    template: 'Products.list',
    waitOn: function () {
      return Meteor.subscribe('products');
    },
    data: function() {
      return Meteor.user().recent.products;
    }
  });
});

However, when I go into the url bar and just completely reload the page from http://localhost:3000/products, it will fail.

What’s the proper way to handle this? I noticed meteor has a method called Meteor.loggingIn(), however, how is it meant to be used in this context?

BTW, my current Publication for current user is simply:

Meteor.publish('currentUser', function () {
  if(this.userId) {
    return Meteor.users.find({ _id: this.userId })
  }
  return [];
});

this might be the problem.


#2

The problem you are seeing is due to the fact that the user isn’t published to the client until the DDP connection is successfully established. You have a couple ways that you can handle this… The first one is to guard against falsey values when you expect an object, which is good practice anytime you fetch reactive data from mini-mongo like you are doing.

Router.map(function() {
  this.route('Products.all', {
    path: '/products',
    template: 'Products.list',
    waitOn: function () {
      return Meteor.subscribe('products');
    },
    data: function() {
      var currentUser = Meteor.user()
      return currentUser && currentUser.recent.products;
    }
  });
});

The second option is to install meteorhacks:fast-render which will publish your user data with the html for the initial page. This is my preferred option because it also stops a login screen from flashing up if the user refreshes the page. As a side note, you shouldn’t need to handle publishing the data for the current logged in user. That is taken care of by the accounts-base package.


#3

Unfortunately, it seems like the accounts-ui package doesn’t publish the information I want to publish. It immediately returns without a few fields I want. I couldn’t find the correct configuration options in the docs to change the fields.

But your solution of fast render worked wonders with only one line added; thank you so much!


#4

Oh, yes… I didn’t think about the fact that you might need additional fields.

In that case you can also use a null publication to so that it gets published automatically without the need to subscribe from the client.

Meteor.publish(null, function () {
  if(this.userId) {
    return Meteor.users.find({ _id: this.userId })
  }
  return [];
}, {is_auto:true});

Global reactive object?
#5

I think you mean null, rather than 'null'.

:+1: for the use of {is_auto:true}: I’d not seen that before and had to look in the source. Loving these undocumented features :wink:


#6

Ah, that publish with null is actually a HUGE help and fixed a lot of my code, thank you good sir


#7

yes @robfallows, good catch… I’ve edited to fix the error