How do I get the return value from a meteor method call?

I have a meteor method defined on the client side using the pattern described here https://guide.meteor.com/methods.html#advanced-boilerplate


// files.collection.js

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';

export const mediaFiles = new Mongo.Collection('media_files')

export const method_InsertFile = {
    name: 'media_file.insert',
  
    // Factor out validation so that it can be run independently (1)
    validate(args) {
        console.log('call validation: ', args)
        new SimpleSchema({
            title: { type: String },
            filename: { type: String },
        }).validate(args)
    },
  
    // Factor out Method body so that it can be called independently (3)
    run({ filename, title }) {
        let inserted_object = { title, filename }
        mediaFiles.insert(inserted_object)
        return inserted_object // object of interest
    },

    call(args, callback) {
        console.log('call call method: ', args)
        const options = {
            returnStubValue: true,     // (5)
            throwStubExceptions: true  // (6)
        }
        Meteor.apply(this.name, [args], options, callback);
    }
};
  
if (Meteor.isServer) {
    Meteor.methods({
        // Actually register defined method with Meteor's DDP system
        [method_InsertFile.name]: function (args) {
            method_InsertFile.validate.call(this, args);
            method_InsertFile.run.call(this, args);
        },
    });   
}

The method is being called as shown below


import { method_InsertFile } from '../api/files.collection';

method_InsertFile.call(data, (err, res) => {
    if (err) console.log("Return err ", err)
    else {
        console.log('result: ', res)
    }
})

I want to retrieve the object inserted_object at the end of the method call. I have tried returning a promise from within the call block of the method definition like this

return new Promise((resolve, reject) => {
    Meteor.apply(this.name, [args], options, (err, res) => {
        resolve(res)
    });
})

but result returns undefined.

Any pointers as to whether this is possible and how to accomplish it is appreciated.

You need to return the result from the actual method:

if (Meteor.isServer) {
    Meteor.methods({
        // Actually register defined method with Meteor's DDP system
        [method_InsertFile.name]: function (args) {
            method_InsertFile.validate.call(this, args);
            return method_InsertFile.run.call(this, args);
//          ^^^^^ Add this return here
        },
    });   
}

If you’re following the advanced boilerplate from the guide, why not use validated-method from the next section?

To alleviate some of the boilerplate that’s involved in correct Method definitions, we’ve published a wrapper package called mdg:validated-method that does most of this for you. Here’s the same Method as above, but defined with the package:

It’s the same thing, but much, much easier to use
If anything, the advanced boilerplate section is there to explain/justify why you should use validatedMethod

Thanks. That worked fine.

Yes. I’ve replaced it with validated method.

One thing though, I don’t know how to return the method call. I mean this line return method_InsertFile.run.call(this, args);.

If you’re using the ValidatedMethod class, it automatically registers itself as a meteor method on creation, so you shouldn’t need to call method_InsertFile.run.call

Can you post enough code to show the path from method creation to calling it?

The method is created as I detailed in my question and I call it like this

import { method_InsertFile } from '../api/files.collection';

method_InsertFile.call(data, (err, res) => {
    if (err) console.log("Return err ", err)
    else {
        console.log('result: ', res)
    }
})

The major point of headache is that in the validate method, I have no access to the two return lines in the code samples below

    Meteor.methods({
        // Actually register defined method with Meteor's DDP system
        [method_InsertFile.name]: function (args) {
            method_InsertFile.validate.call(this, args);
            return method_InsertFile.run.call(this, args); // I have no access to this line.
        },
    });   
}
    run({ filename, title }) {
        let inserted_object = { title, filename }
        mediaFiles.insert(inserted_object)
        return inserted_object // I have no access to this line.
    },

This looks like you’re still using the boilerplate and not ValidatedMethod. If you’re using validatedMethod, you can skip the second code block entirely.

What do you mean by you “have no access to this line”?
Are you wanting to do validation after inserting the new object in the database? why?

I want the inserted object returned because I’m using redux to manage my local state so I need the object to update the state.

But I think I found another way to update the state without returning the inserted object, which would allow me used validated method.

I’ll update here once I do that and it works.

I think there must be something else in your code missing, because from what you’ve posted, the inserted object will be returned as res in the callback of call.

This is usually the most simple part of a method, since it is one of their primary purposes.

If you are still running that second code block, there might be a bug from the collision between your code repeating work already done by ValidatedMethod.

Alternatively, is it possible that mediaFiles.insert(inserted_object) is modifying the object? Are you using Collection2 to clean and validate database methods?