Client calling findOne in Server Method

Hi!

I have a client-side form that can create a document upon submission. I want to see if one of the input fields already exists on a Document in the DB though. This would then alert the user and ask them if they want to continue creating the record.

Client-side event

Template.createDoc.events({
    'click button[type=submit]'(e, template) {

        //This particular example is checking to see if a Doc with its `name` property set to `value` already exists

        const value = $('#name');
        const fieldName = 'name';

        const exists = Meteor.call('checkIfFieldExistsOnDoc', fieldName, value);
        if (exists) {
            if (confirm(`Doc with ${value} as its ${fieldName} already exists. Are you sure you want to continue creating Doc?`) {
               //db.Docs.insert....
           }
        }
    }
});

Server-side Meteor Method

'checkIfFieldExistsOnDoc'(field, val) {
    
    if (this.isServer) {
      this.unblock();
      check(field, String);
      check(val, String);

      if (!this.userId) {
        throw new Meteor.Error('not-authorized', 'You are not authorized.');
      }

      const findObj = {};
      findObj[field] = val;

      const fieldsObj = {};
      fieldsObj[fieldsObj] = 1;

      const doc = Docs.findOne(findObj, {fields: fieldsObj});
      return doc;
    }
  },

My issue is that the client-side code always gets undefined back when calling the Server method. I understand client-side call invocations are asynchronous and require a cb, however, I’m not keen on wrapping all of my subsequent client-code into a callback yet. I know removing the param on server adds some syntactical sugar to make it ‘synchronous’ kinda like await … So - just wondering if there was way to do that on client-side too

So - any other ideas on how I can attempt to do this simple feature?

Also - I was thinking of having the client-side page’s onCreated do a 1-time server call to get ALL names for all Docs, storing this in memory, and then doing the check upon form submission using this. Obviously, this is inefficient and not-scalable, although it would work

You can put the method definition in a server directory and only import that file from meteor server at startup.
Your method code will be secured and there is no invocation for the client.

Client side method calls are asynchronous and always return undefined. You can specify a callback for when the method returns

Meteor.call('checkIfFieldExistsOnDoc', fieldName, value, (exists) => {
        if (exists) {
            if (confirm(`Doc with ${value} as its ${fieldName} already exists. Are you sure you want to continue creating Doc?`) {
               //db.Docs.insert....
           }
        }
});

@jamgold, Thanks, but I was hoping for some kinda async/await syntactical sugar so that I don’t need to use the cb.

Check the Meteor documentation for Methods. It says

On the client, if you do not pass a callback and you are not inside a stub, call will return undefined , and you will have no way to get the return value of the method. That is because the client doesn’t have fibers, so there is not actually any way it can block on the remote execution of a method.

1 Like

Override the Meteor call function with a version that converts the original method into a promise that resolves with the results.

You still need to work with the callback once when overriding the original call function. The new function now works with async/await

2 Likes