I have Twilio Studio calling a Twilio Function and need it to send email to a variable list (small list) of emails. This question is mostly around looping through them as I can pass variables just fine. I have an array of emails to send a text to and am in a Twilio Function. But all examples I find online are about sending to just ONE. Part of me thinks this needs to be a Twilio Function calling another Twilio function (one loops, the other sends Emails)... but I can't figure out a way to do that. If I could contain it to one Twilio function, that would be great.
I have Twilio Studio calling a Twilio function. I need to keep this all on Twilio... so looping through via PHP and running functions one at a time through there doesn't work. I need this to run on Twilio's serverless setup.
Here's an example of what I have that works:
exports.handler = function(context, event, callback) {
// using SendGrid's v3 Node.js Library
// https://github.com/sendgrid/sendgrid-nodejs
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
const msg = {
to: 'me@example.com',
from: 'noreply@example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
sgMail.send(msg).then(response => {
let twiml = new Twilio.twiml.MessagingResponse();
callback(null, twiml);
})
.catch(err => {
callback(err);
});
};
Here's me trying to loop through in similar fashion and failing
exports.handler = function(context, event, callback) {
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
var responder_emails = 'me@example.com,me+test1@example.com';
var emails_a = responder_emails.split(',');
emails_a.forEach(function(responder_email) {
const msg = {
to: responder_email,
from: 'noreply@example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
sgMail.send(msg);
});
callback();
};
I can pass in multiple emails into a Twilio function... I'm just not sure how to loop through correctly.
Heyo. Twilio Evangelist here. 👋
In your first example, you rightfully waited for the send
call to be done by using then
. In your second example, you missed that. You run several send
calls but immediately call callback
without waiting.
A fixed (roughly prototyped version) could look as follows.
exports.handler = function(context, event, callback) {
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(context.SENDGRID_API_KEY);
var responder_emails = 'me@example.com,me+test1@example.com';
var emails_a = responder_emails.split(',');
Promise.all(emails_a.map(function(responder_email) {
const msg = {
to: responder_email,
from: 'noreply@example.com',
templateId: 'my-id-goes-here',
dynamic_template_data: {
recipient_name: 'John Smith'
}
};
return sgMail.send(msg);
})).then(function() {
callback();
}).catch(function(e) {
callback(e);
})
});
You have already an array of emails because you called split
. You can use this array in combination with Array.map
and Promise.all
.
Map basically iterates over your array and lets you create a new array with whatever you return from the function inside of map. What the code above does is that it transforms [email, email]
to [Promise, Promise]
. The promises are the return value of sgMail.send
.
Now, that you have an array holding promises that will resolve when sendgrid accepted your call, you can use Promise.all
. This method waits for all the promises to be resolved (or rejected) and returns itself a new promise which you can use then
with. When all sendgrid calls are done it's time to finish the function by calling the function callback
.
Side note: this "map/Promise.all" trick performs all send grid calls in parallel. There might be situations where you want to call them one after another (saying you are doing a lot of calls and run into rate limiting).
Hope that helps and let me know how it goes. :)