Using wrapAsync with bindEnvironment correctly?


#1

I’ve got an app that uses the Stripe API to do something like this:

var createCustomer = Meteor.wrapAsync(function(userId, token, cb) {
  Stripe.customers.create({
    description: 'User id: ' + userId,
    source: token
  }, Meteor.bindEnvironment(function(error, customer) {
    // do something with error or customer
    cb(...)
  }));
}),

Is that the right way to do it?


#2

I don’t think you need to do the bindEnvironment unless you actually need to execute Fiber-related code before calling the callback. The cb is Fiber-wrapped already I think. Have you tried that?

Also, why don’t you just do:

var createCustomer = Meteor.wrapAsync(Stripe.customers.create);
// Yay.. new customer
var customer = createCustomer({
  description: 'User id: ' + userId,
  source: token,
  // More options.. maybe
});

#3

For Meteor.wrapAsync you pass it the function you want to wrap, and the thisArg. If your function contains a node-style callback, you can simply wrap the function immediately without creating your own.

Meteor.wrapAsync(Stripe.customers.create, Stripe.customers)({
  description: 'Account for user',
  source: token
});

I think with Stripe in particular, it returns a promise. In 1.3, you can make this a bit more syntactically friendly

Meteor.methods({
  'Stripe.customers.create': async function (userId, token) {
    check(userId, String);
    check(token, String);

    const customer = await Stripe.customers.create({
      description: `User ${userId}`,
      source: token
    });

    return StripeCustomers.insert(customer); // insert into your own db
  }
});

Meteor.bindEnvironment is generally used in situations where you have a callback and you’re performing some action asynchronously. For example, a node socket.

import {Socket} from 'net';
const socket = new Socket();
socket.on('connect', Meteor.bindEnvironment(() => {
  console.log('Successfully connected');
})).connect({
  port: 1560,
  host: 'localhost'
});

#4

Ah, I didn’t realize that. Problem solved with async/await. Nice! Thanks. :slight_smile:

Does Meteor support async functions for Meteor.methods in 1.3?


#5

Does Meteor support async functions for Meteor.methods in 1.3?

Yes, it’s much easier than using Meteor.wrapAsync, I’ve been using it heavily


#6

But, I mean, is it officially supported, as in Meteor.call() will return the async function’s eventual return value? I just gotta try it out! x]

EDIT, well, we can just await Meteor.call() on the server-side as it should return the promise! If not officially supported, then the client-side Meteor.call() would return undefined instead of a promise, so this will work server-side only. Gah, I better just get off my phone and go try it. :laughing:


#7

@corvid that is how I fixed my problem. In order to get to the body of the request I needed to wrap the wrap the callbacks into Meteor.bindEnvironment. Here is a working version for Iron:Router

Router.route('receiveMerchantUpdate', async function receiveMerchantUpdate() {
    // NodeJS request object
    var req = this.request;
    // NodeJS  response object
    var res = this.response;
    let rawData = '';
    req.on('data', Meteor.bindEnvironment((chunk) => { rawData += chunk; }));
    req.on('end', Meteor.bindEnvironment(() => {
      try {
        const json = JSON.parse(rawData);
        handleStripeWebhook(json);
      } catch (e) {
        console.error(e.message);
      }
    } ));
    return res.end();
},{
  where: 'server',
  path: 'stripe/webhooks'
});