Search code examples
node.jspromisenode-promisify

Include a Promisified Callback Inside A Promise Resolver?


I'm setting up a GraphQL resolver to call a Braintree endpoint. The Braintree npm package wants to call their endpoint with code that looks like this:

braintreeGateway.customer.create({
    firstName: "Jen",
    lastName: "Smith",
    company: "Braintree",
    email: "[email protected]",
    phone: "312.555.1234",
    fax: "614.555.5678",
    website: "www.example.com"
}, function (err, result) {
    result.success;
    result.customer.id;
});

GraphQL resolvers return promises. I'm trying to figure out how to promisify this callback, and include it within a promise resolver.

I've read a lot of SO posts about promisifying a callback, but the ones I've found so far don't seem to quite match up to this use case.

I've tried a lot of things, this being the latest:

getBrainTreeCustomerId: (parent, args, context) => {
    const userid = context.userId;

    const braintreeCustomerCreate =  util.promisify(braintreeGateway.customer.create);

    async function run_braintreeCustomerCreate() {
        try {
            let braintreeCustomerId = await braintreeCustomerCreate({
                firstName: "Jen",
                lastName: "Smith",
                company: "Braintree",
                email: "[email protected]",
                phone: "312.555.1234",
                fax: "614.555.5678",
                website: "www.example.com"
            });
            return braintreeCustomerId
        }
        catch (err) {
            console.log('ERROR:', err);
        }
    }

    return Promise.resolve()
        .then(() => {
            let braintreeCustomerId = (async () => {
                let braintreeCustomerId = await run_braintreeCustomerCreate()
                return braintreeCustomerId;
            })();
            return braintreeCustomerId;
        })
        .then((braintreeCustomerId) => {
            return braintreeCustomerId;
        })
        .catch((err) => {
            console.log(err);
        });
}
}

But the catch handler gets an error saying "Cannot read property '_createSignature' of undefined".

What's the correct syntax to use here?


Solution

  • By setting the promisified function outside of the scope of the gateway you might be separating it too much from the BrainTree object structure. The error sounds like it's trying to access another internal function that's no longer in scope. Try this instead without using util.promisify:

    async function run_braintreeCustomerCreate() {
      return new Promise((resolve, reject) => {
        braintreeGateway.customer.create( 
          {
            firstName: "Jen",
            lastName: "Smith",
            company: "Braintree",
            email: "[email protected]",
            phone: "312.555.1234",
            fax: "614.555.5678",
            website: "www.example.com"
          },
          (err, result) => {
            if (err) return reject(err);
            return resolve(result);
          }
        );
      });
    }
    

    This will return a Promise that either resolves or rejects based on the result of the call while calling in a way consistent with the object.

    Another way that may work would be to add the promisified function to the customer object like this:

    const createAsPromise = util.promisify(braintreeGateway.customer.create);
    braintreeGateway.customer.createAsPromise = createAsPromise;
    
    async function run_braintreeCustomerCreate() {
      try {
        let braintreeCustomerId = await braintreeGateway.customer.createAsPromise(
          { /* data */ }
        ); 
        return braintreeCustomerId;
      }
      catch(err) console.log('ERROR:', err);
    }          
    

    As a general note, you don't need to do catching earlier in the chain as the .catch will work and it simplifies your logic by removing the need for the try at all. Also note that the object returned from the create call will contain an Id but is not itself just the Id - it's an object. In fact having the try with catch there will block the error from being seen by whoever calls it because it will end up returning nothing (ie, undefined) as the resolved value.