[Solved] Erros publishing a collection

Hi All,

I am trying to understand publishing in Meteor. I have this collection called “User intakes” that I publish this way

    function isStudent(user_id){
      var student = false
      var user_object = Meteor.users.findOne({_id: user_id})

      if(user_object.roles.toString() == 'student'){
        student = true
      }

      return student
    }

startup.js

Meteor.publish("UserIntakes", function () {

  if(isStudent(this.userId)){
    //If the user is a student only sent their intake result
    return UserIntakes.find({user_id: this.userId});
  }else{
    //send intake results of everyone to advisors and admins
    return UserIntakes.find();
  }
  
});

but i get this error on the server:

Exception from sub UserIntakes id 2i9dPHJDcmAfABjn9 { stack: 'TypeError: Cannot read property \'roles\' of undefined\n    at isStudent (app/server/startup.js:18:19)\n    at [object Object].<anonymous> (app/server/startup.js:97:10)\n    at packages/matb33_collection-hooks.js:299:21\n    at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)\n    at [object Object]._handler (packages/matb33_collection-hooks.js:298:28)\n    at maybeAuditArgumentChecks (packages/ddp/livedata_server.js:1617:1)\n    at [object Object]._.extend._runHandler (packages/ddp/livedata_server.js:950:1)\n    at [object Object].subscriptionProto._runHandler (packages/meteorhacks:kadira/lib/hijack/wrap_subscription.js:12:1)\n    at [object Object]._.extend._startSubscription (packages/ddp/livedata_server.js:769:1)\n    at [object Object]._.extend.protocol_handlers.sub (packages/ddp/livedata_server.js:582:1)\n    at packages/meteorhacks:kadira/lib/hijack/wrap_session.js:77:1\n    at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)\n    at [object Object].sessionProto.protocol_handlers.sub (packages/meteorhacks:kadira/lib/hijack/wrap_session.js:76:1)\n    at packages/ddp/livedata_server.js:546:1',
I20160118-14:59:47.174(1)?   source: 'subscription' }

not sure what I am doing wrong. Any help would be greatly appreciated

Meteor.users.findOne({_id: user_id}) isn’t returning anything, so calling .roles on undefined is throwing an exception. Its hard to tell without more context what the chain of events is here, but I would try and add some code to check if your actually logged on at point you publish, your post suggests that you are doing this on startup (hard to tell for sure), which might be happening before you are logged in.

I’f its that you are not logged in, i would change:
if(isStudent(this.userId)){

to

if(this.userId && isStudent(this.userId)){

Of course, when you subscribe to this publication is also important.

@floydprice Yes you are correct. this is before I am logged into the app and you are also correct this is done on startup.

What is the correct way to publish collections that I need published only when someone is logged in? Do I just do and “if” check for this.userId and if it does not exist publish nothing and if it does publish the result of the Query or is there another best practice for that scenario?

Thank for all your help

The canonical way to do this is in the Meteor Guide. Basically, use:

if (!this.userId) {
  return this.ready();
}

within your publication to return an “empty” result.

If you want to handle this more thoroughly, you could look at the Guide section on security: publications and userId.

@robfallows Thank you very much

1 Like

As @robfallows says the best practice is to yield an empty result, however i’d ask you to look at your design a little as you actually appear to be using your publication for two discrete use cases which isn’t really the idea.

If you step back and look at your pub, you are actually about to give logged in users less data (but the same context) than logged out users, this feels like an anti pattern that would be better resolved with two publications. however i think (and correct me if i’m wrong) what you are really hoping to do is give students a subset of the data and non-students (i.e. more privileged users) all the data, either way you really are relying on a user being logged in, and the only way to be sure of that is to wait (client side) until you have an actual user object before you subscribe.

So in summary, think about what your trying to publish and the state you require to achieve this, always ensure that you never leak more data than you intend too, (if userId is null return this.ready) and construct your client side subscription in a way that lets your application be in the correct state (don’t subscribe until you are logged in).

Without seeing your code and knowing the architecture, i can’t be specific but its likely that you will want to use a template level subscription rather than a global one given your requirement on a logged in user.

2 Likes

@floydprice YES!!! You got what I am trying to do, and you answered the question I was trying to ask and did not know how to put it. Yep I am trying to subscribe only when I am logged in, that will solve my problem. I was a little confused on if the server will do work based on my publishing alone, but my understanding now is that until i subscribe I am not puling data to minimongo. Good tip on having multiple subscriptions too.

This helps a lot Thank you!

1 Like