this.userId is sometimes null (publications and methods)

#1

Hi guys!

So I am having some weird problems with my (React) app.
In my very top component (App), I use a Meteor data container to return the prop currentUser from Meteor.user() … and only when this returns a truthy value, I render anything in the app. --> The user should ALWAYS be logged in before anything happens. ( I hope this solution makes sense to you?!)

The weird thing is, that in some cases some publications and method calls return null for this.userId. This completely breaks my app in these cases for obvious reasons.

Does anybody have any idea why this might be happening and how to fix this?!

Thanks!
best, P

#2

I cannot see the details of your methods and publications, but keep in mind that they are just api endpoints. Like in REST api’s they are always publicly accessible. If a non-logged-in user tries to access these endpoints. The this.userId property will be null.

#3

And that is why you need to check everything coming out of a reactive method for null values.

#4

I do check() all the method/publication params!
And I am sure that this this.userId == null “errors” occur in “my code” (subscriptions / method calls), there is nobody from outside accessing these methods/pubs believe me!
What am I supposed to do in this case? in my pub I am now just returning this.ready() when this.userId is null but then what? The subscription of my meteor data container is not rerun automatically, the client does not receive the appropriate data and then what? The user has to reload the whole page?! this can’t be the best solution.

I just do not understand how this can happen …

#5

Ok lets take one step back. Just to clarify:

  1. You’ve created a login system
  2. AFTER login, the known users subscribe to the data
  3. Publication contains this.userId

Any other scenario will result in this.userId === null. What developers often encounter when they are new to full-stack reactivity is this scenario:

  1. User accesses app
  2. Client subscribes to data
  3. Publication has no userId (this.userId === null)
  4. User logs in
  5. Client re-subscribes to data
  6. Publication now contains the userId
  7. User gets data

If its not the above then you will have to share the part where you subscribe on the client side and the publication. If its indeed the above fail scenario then this would be an example to solve it in Blaze:

Template.foo.onCreated(function()) {
    this.autorun(() => {
        if(Meteor.userId()) { //Only subscribe if logged in
            this.subscribe('fooPublication');
        }
    });
});

Or in React:

export const Foo = createContainer(() => {
    Tracker.autorun(() => {
         if(Meteor.userId()) { //Only subscribe if logged in
             Meteor.subscribe('fooPublication');
         }
    });

    ...
});

Also always check if this.userId !== null on the publications and method if you want to limit access to logged in users only. This is a standard practice. “You must be logged in” errors are normal behaviors from server side.

#6

Thanks a bunch for your help @cloudspider!
So yeah it works like that in my case:

My App React Layout (== Component) is the very top component for my whole app. This does not include login pages. So if the user is not logged in when he tries to access any route in the App layout-route, I redirect him to my login page.
The App component itself uses the meteor data container to get the value of Meteor.user() and assign it to my prop currentUser (this is then also assigned to this.context.currentUser and so on).
In the App render() function I only render any this.props.children if currentUser is set, thus any subscriptions or whatever of child components only happen when the user is successfully logged in and Meteor.user() returns the user.

does this make sense to you?

So tl;dr:
Yes, I do check for logged in user and the publications/method calls in question should definitely only called by logged in users!

PS: This whole system is based on themeteorchef/base and uses Meteor’s standard accounts package.

#7

So I am running into a similar problem again:

-) main app runs at localhost:3000 - I am logged in
-) second app runs at localhost:3030, User is also logged in, also connected to localhost:3000 via const myDDP = DDP.connect()
-) second app calls myDDP.subscribe(), while console.log(Meteor.userId()) right before the subscription returns the correct value of the logged in user
-) main app recognizes the subscription, but this.userId or Meteor.userId() are both null!

I don’t get it. where is my error?

Connect to different (running) Meteor server in dev