Server Method.call() to another Method with userId

Is there any way to do a server Meteor.call('mymethod') and pass through this or at least this.userId?

With a validated method, you can _execute() and pass in a context link

This is something I need to accomplish the following:

  • from a client&server accessible validated-method
  • when running on the server
  • also call a different server-only method.

The problem is, I can not import the server only method into shared code and call it directly.

And I can not expose a server only method without authentication / security.

So how do I get a server-side “trusted” secure userId to pass through to another server method?

// is common (both server and client)
const MSchema = new SimpleSchema({
  a: {
    label: 'Argument',
    type: String,
    optional: true,
  },
});
const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
    console.log('M0', params.a, this.userId);
    Meteor.call('M1', params.a);
  },
});
// isServer
Meteor.methods({
  M1: function m1(a) { check(a, String); console.log('M1', a, this.userId); Meteor.call('M2', a); },
  M2: function m2(a) { check(a, String); console.log('M2', a, this.userId); },
});
// isClient & authenticated
M0.call({ a: 'I wish userId would just pass through' });
Meteor.call('M1', 'I wish userId would just pass through');

Rather than using one meteor method to call another, I would extract M2’s functionality into a separate function that is used by both M2 and called from M1. This function can operate based on a userId that is passed to it as a parameter.

1 Like

I agree - but at some point, I have to pass from common “validated method code” into “server only” code… that is somewhat sticky… because the validated method code (on both client and server) can not import server-only code.

I know of no other way besides a method call, to make that happen.

Your question is pretty abstract, can you give a more concrete example? By the way I’ve used your throttle packages in multiple projects :+1:

1 Like

How about this?

const MSchema = new SimpleSchema({
  a: {
    label: 'Argument',
    type: String,
    optional: true,
  },
});
const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
    console.log('M0', params.a, this.userId);
    if(Meteor.isServer){
      m1(params.a, this.userId);
    }
  },
});
// isServer

function m1(a, userId) { check(a, String); console.log('M1', a, userId); m2(a, userId); },
function m2(a, userId) { check(a, String); console.log('M2', a, userId); },

Meteor.methods({
  M1: function insidemm1(a) { check(a, String); m1(a, this.userId) },
  M2: function insidemm2(a) { check(a, String); m2(a, this.userId) },
});

// isClient & authenticated
M0.call({ a: 'I wish userId would just pass through' });
Meteor.call('M1', 'I wish userId would just pass through');

Concrete example:

  • ValidatedMethod on both client and server, adds a record.
  • Secure, server only “cleanup” on that new record, requires authentication.
  • Client triggers ValidatedMethod
  • if (!this.isSimulation) Meteor.call('myOtherMethod', { _id })

You know - truth is - the test code I pasted above works as I would have expected… it does pass through the userId from client to server (first invocation) and does also pass it through to subsequent calls, on server --> server.

So, in short, I can confirm it’s doing what I thought it should be doing and passing through the Meteor context server --> server via Meteor.call()… something in my function chain is loosing it.

Sadly - I can’t import m1 into client side code…

In that case, just move M0 onto the server, and call it w/ Meteor.call. Or if you want to simulate something, have a different version of M0 (without the call to m1()) on the client, and one with the call to m1() on the server:

//isClient
const MSchema = new SimpleSchema({
  a: {
    label: 'Argument',
    type: String,
    optional: true,
  },
});

// isServer

const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
    console.log('M0', params.a, this.userId);
    m1(params.a, this.userId);
  },
});

function m1(a, userId) { check(a, String); console.log('M1', a, userId); m2(a, userId); },
function m2(a, userId) { check(a, String); console.log('M2', a, userId); },

Meteor.methods({
  M1: function insidemm1(a) { check(a, String); m1(a, this.userId) },
  M2: function insidemm2(a) { check(a, String); m2(a, this.userId) },
});

// isClient & authenticated
Meteor.call('MethodUserIdTesting', { a: 'I wish userId would just pass through' });
Meteor.call('M1', 'I wish userId would just pass through');

or

//isClient
const MSchema = new SimpleSchema({
  a: {
    label: 'Argument',
    type: String,
    optional: true,
  },
});

const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
    console.log('M0', params.a, this.userId);
  },
});

// isServer

const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
    console.log('M0', params.a, this.userId);
    if(Meteor.isServer){
      m1(params.a, this.userId);
    }
  },
});

function m1(a, userId) { check(a, String); console.log('M1', a, userId); m2(a, userId); },
function m2(a, userId) { check(a, String); console.log('M2', a, userId); },

Meteor.methods({
  M1: function insidemm1(a) { check(a, String); m1(a, this.userId) },
  M2: function insidemm2(a) { check(a, String); m2(a, this.userId) },
});

// isClient & authenticated
M0.call({ a: 'I wish userId would just pass through' });
Meteor.call('M1', 'I wish userId would just pass through');

I agree - that’s what I’d normally do, but I’ve adopted validated methods, and have a factory to create them now, and it’s hard to want to abandon that.

Can’t you still use them, but have a different version on the client and the server?

1 Like

That is true… I will consider that… thanks for talking it through with me.

(I’ve internalized my own convention of meteor methods for server and validated-methods for both… but it doesn’t have to be that way)

there is another way…

const M0 = new ValidatedMethod({
  name: 'MethodUserIdTesting',
  mixins: [simpleSchemaMixin],
  schema: MSchema,
  schemaValidatorOptions: { clean: true, filter: true },
  run: function m0(params) {
   
    if(Meteor.isServer){
      const m1 = require("/server/stuff/my_m1").default;
      m1(params.a, this.userId);
    }
  },
});

@macrozone that isn’t quite as “ES2015 sexy”, but seems like it might be the simplest solution… thanks!

1 Like