javascriptmeteormeteor-methods

Meteor Method callback getting undefined for result


I haven't coded in Meteor in a while, but I have this Meteor method that creates a task and returns the ID and another that appends that task to a project:

Meteor.methods({
  createTask(task) {
    // TODO Add a check here to validate
    Tasks.insert(task, (err, id) => {
      if (err) {
        throw new Meteor.Error(err);
      }
      id = {id: id};
      console.log('Returning id: ', id);
      return id;
    });
  }
});

Meteor.methods({
  appendTaskToProject(projectId, taskId) {
    // TODO Add check here to validate
    const project = Projects.findOne({_id: projectId});
    if (project) {
      if (!project.tasks) {
        project.tasks = [];
      }
      project.tasks.push(taskId);
      Projects.update({_id: projectId}, project, (err, id) => {
        if (err) {
          throw new Meteor.Error(err);
        }
      });
    } else {
      throw new Error("Could not find project");
    }
  }
});

I am attempting to call it on the client like thus:

Meteor.call('createTask', task, (err, taskId) => {
  console.log('err: ', err);
  console.log('taskId: ', taskId);
  if (err) {
    this.setState({ error: err.message});
  } else {
    Meteor.call('appendTaskToProject', projectId, taskId, (err, result) => {
      if (err) {
        this.setState({ error: err.message});
      } else {
        this.setState({ newTaskDialogOpen: false })
      }
    });
  }
});

The problem I am having is that taskId is not set in the callback. From the method-side I see the log message inthe server like:

I20180110-07:30:46.211(-5)? Returning id:  { id: 'a3nS9GcRhuhhLiseb' }

And in the client:

Returning id:  {id: "a3nS9GcRhuhhLiseb"}id:
Tasks.jsx:43 err:  undefined
Tasks.jsx:44 taskId:  undefined

So I know it's returning something, but the callback is just not getting it. I know I should probably change the createTask to just take the task AND the projectId to link it too, but I would like to try and figure out why it's not getting the results of the Meteor method into the callback on the client-side.


Solution

  • The Meteor API documentation on collection methods like insert says the following:

    On the server, if you don’t provide a callback, then insert blocks until the database acknowledges the write, or throws an exception if something went wrong. If you do provide a callback, insert still returns the ID immediately. Once the insert completes (or fails), the callback is called with error and result arguments. In an error case, result is undefined. If the insert is successful, error is undefined and result is the new document ID.

    Applying this information to your code would create the following:

    Meteor.methods({
      createTask(task) {
        // TODO Add a check here to validate
        return Tasks.insert(task, (err, id) => {
          if (err) {
            throw new Meteor.Error(err);
          }
        });
      }
    });
    

    This would return the new generated ID immediately but will have the disadvantage, that the error is thrown afterwards. Therefore you better be going the direct way and execute "sync-like":

    Meteor.methods({
      createTask(task) {
        // TODO Add a check here to validate
        return Tasks.insert(task);
      }
    });
    

    The meteor method automatically wraps the code so that on a positive return your client will receive a null value for error and the _id value for result. If an error occurs during the insert, that method will automatically return the error in the client callback as error and the reuslt will be null.

    If you are concered with the synchronous nature of the code read this part of the guide about methhods.

    Same should apply for your update method:

    Meteor.methods({
      appendTaskToProject(projectId, taskId) {
        // TODO Add check here to validate
        return Projects.update({_id: projectId}, {$push: {tasks: taskId});
      }
    });
    

    Note, that I summarized this method to a more mongo oriented approach.