Reputation: 6029
This Meteor server code tries to use Meteor.userId() in public method "sendEmail", but sometimes I get the error
Error Meteor.userId can only be invoked in method calls. Use this.userId
lib = (function () {
return Object.freeze({
'sendEmail': function(msg){
let userId = Meteor.userId();
//do stuff for this user
},
'otherPublicMethod': function(){
//do other things then use sendEmail
lib.sendEmail(); // <---- Error Meteor.userId can only be invoked in method calls. Use this.userId
}
});
}());
// Now I call sendEmail from any where, or can I?
Meteor.methods({
'sendEmail': (msg) => {
lib.sendEmail(msg); // <---- NO error when this is called
},
});
How can it be fixed? thx
Upvotes: 0
Views: 604
Reputation: 2386
i'm going to gently suggest you replace your use of IIFE. instead, you can take advantage of ES16 modules to define your common functions.
as you've indicated, Meteor.userId() is available in method calls, but won't be available in standalone functions on the server. the pattern i use, when invoking such functions from a method call, is to pass in the userId (or actual user). e.g.
imports/api/email/server/utils/emailUtils.js:
const SendEmail = function(userId, msg) {
// do stuff
};
export {SendEmail};
imports/api/email/server/emailMethods.js:
import {SendEmail} from '/imports/api/email/server/utils/emailUtils';
Meteor.methods({
'sendEmail': (msg) => {
check(msg, String);
// other security checks, like user authorization for sending email
SendEmail(Meteor.userId(), msg);
},
});
now, you have a re-usable SendEmail function you can call from any method or publish. additionally, by following this pattern, you're one step closer to creating testable code. i.e. it's easier to test a function into which you're injecting a userId than it is to mock "this.userId" or "Meteor.userId()".
Upvotes: 1
Reputation: 31
If lib.sendEmail is being called from any async method, then ensure that you bind Meteor environment e.g. check code below which simulates the async behaviour
lib = (function () {
return Object.freeze({
'sendEmail': function (msg) {
let userId = Meteor.userId();
console.log(userId);
//do stuff for this user
},
'otherPublicMethod': function () {
//do other things then use sendEmail
lib.sendEmail(); // <---- Error Meteor.userId can only be invoked in method calls. Use this.userId
}
});
}());
// Now I call sendEmail from any where, or can I?
Meteor.methods({
'sendEmail': (msg) => {
//simulate async behaviour + bind environment
Meteor.setTimeout(Meteor.bindEnvironment(function () {
lib.sendEmail(msg); // <---- NO error when this is called
}));
//output :
// null - if user has not logged in else
// actual userId - if user is loggedin
//simulate async behaviour without binding environment
Meteor.setTimeout(function () {
lib.sendEmail(msg); // <---- error when this is called
});
//output :
// Exception in setTimeout callback: Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
},
});
Upvotes: 1