[Solved] Meteor.userId can only be invoked in method calls or publications

I try to use Meteor.userId() in hook of mongoose.

// Save
Schema.pre('save', async function () {
  if (this.isNew) {
    let userId = await Meteor.userId()
    console.log('Meteor UserId', userId);
  }
}); 

// Update
Schema.pre('updateOne', async function () {
    let userId = await Meteor.userId()
    console.log('Meteor UserId', userId);
});

// Remove
Schema.pre('remove', { query: true, doc: true }, async function () {
    let userId = await Meteor.userId()
    console.log('Meteor UserId', userId);
});

But it’s error Meteor.userId can only be invoked in method calls or publications

I’m not super familiar with how Mongoose intercepts these calls - however, Meteor.userId needs to be able to determine the connection that issued the command. You MIGHT have better luck wrapping your function in Meteor.bindEnvironment however, I don’t know how nicely that will play with async. Why do you need to access the userId in your hooks?

Track Audit log all user events on transaction (Save,Update,Remove), createdBy:userId

I don’t know how Mongoose hooks are called but I guess you can call a method from within them

Schema.pre('save', async function () {
  if (this.isNew) {
    Meteor.call('mySaveMethodThatKnowsTheUserId')
  }
}); 

I don’t think this will work - the Meteor.call would have the userId of the parent context (which in this case seems to be undefined)

My outlook on this would be to just not do this work in a hook and pull the userId from a method call or some place that has access to the id.

2 Likes

My guess is that you have to explicitly pass the userId from where you save your doc. E.g.

Meteor.Methods({
  test() { const item = new Item(); item.save(this.userId, () => {}) }
});

// somewhere else
item.pre('save', function (next, userId, callback) {
  console.log(userId);
  next(callback);
});

Thank you , I will try .

How about Update and Remove ?

it’s work .

Meteor.Methods({
  removeMethod(doc) {
       const removeDoc = new Collections(doc); 
       removeDoc.remove({ userId: this.userId })
  },
  updateMethod(doc) {
       return Collections.updateOne({ _id: doc._id }, { $set: doc }, { userId: this.userId })
  }
});

// somewhere else for remove
item.pre('remove', function (next, userId) {
  console.log(userId);
});

// for update one
Schema.pre('updateOne', async function () {
  let { options: { userId }} = this
  console.log('userId', userId);
});
1 Like