56

I am trying to send email to multiple recipients. For this I have created an array of recipients, but with my code I am only able to send mail to last email ID of the array three times. What's wrong with my code?

var nodemailer = require("nodemailer");

var smtpTransport = nodemailer.createTransport(
"SMTP",{
  host: '',
  //  secureConnection: true,         // use SSL
  port: 25
});

var maillist = [
  '****.sharma3@****.com',
  '****.bussa@****.com',
  '****.gawri@****.com',
];

var msg = {
    from: "******", // sender address
    subject: "Hello ✔", // Subject line
    text: "Hello This is an auto generated Email for testing  from node please ignore it  ✔", // plaintext body
    cc: "*******"    
    //  html: "<b>Hello world ✔</b>" // html body
}


maillist.forEach(function (to, i , array) {
  msg.to = to;

  smtpTransport.sendMail(msg, function (err) {
    if (err) { 
      console.log('Sending to ' + to + ' failed: ' + err);
      return;
    } else { 
      console.log('Sent to ' + to);
    }

    if (i === maillist.length - 1) { msg.transport.close(); }
  });
});
Łukasz
  • 2,131
  • 1
  • 13
  • 28
Atul Sharma
  • 563
  • 1
  • 4
  • 4

9 Answers9

98

nodemailer docs say:

to - Comma separated list or an array of recipients e-mail addresses that will appear on the To: field
cc - Comma separated list or an array of recipients email addresses that will appear on the Cc: field
bcc - Comma separated list or an array of recipients email addresses that will appear on the Bcc: field

so you can just do:

var maillist = [
  '****.sharma3@****.com',
  '****.bussa@****.com',
  '****.gawri@****.com',
];

var msg = {
    from: "******", // sender address
    subject: "Hello ✔", // Subject line
    text: "Hello This is an auto generated Email for testing  from node please ignore it  ✔", // plaintext body
    cc: "*******",
    to: maillist
}
Philipp Kyeck
  • 18,402
  • 15
  • 86
  • 123
50

As far as I know you will be able to get multiple recipients like this

"mail1@mail.com,mail2@mail.com,mail3@mail.com,mail4@mail.com"

So why you don't do something like

var maillist = '****.sharma3@****.com, ****.bussa@****.com, ****.gawri@****.com';

var msg = {
    from: "******", // sender address
    to: maillist,
    subject: "Hello ✔", // Subject line
    text: "Hello This is an auto generated Email for ...  ✔", // plaintext body
    cc: "*******"    
    //  html: "<b>Hello world ✔</b>" // html body
}

I have already tried and it is working. Furthermore, from my point of view, why you have to worry about "asynchronously" or sending emails 1K times if you have the capability of sending all of them only in once without any complication?

Anyway hope this help, answer your question or it may help somebody else

Surely, my answer can be improved..

ackuser
  • 5,681
  • 5
  • 40
  • 48
  • yes this also be possible but , I don't know in advance the TO list of recipient , there can be any number of recipients . – Atul Sharma Apr 02 '15 at 17:43
  • But you are getting these recipients from somewhere else...I mean you are getting for a form or a variable I think...By the way what I think you could add some kind of pre-process on the emailist before you are sending any emails so you get all them in once...I am putting a plunkr with the idea and format of how getting the variable maillist completely format and just to put on "msg" before send the email http://plnkr.co/edit/giNSNHJTlrEi5nj8WmG5 – ackuser Apr 03 '15 at 08:24
  • 1
    and how pass variable from js to Html file ?? – Pardeep Jain Feb 15 '18 at 12:09
  • 12
    Keep in mind this method (`to` field with multiple addresses) exposes all email recipients to all receivers. Privacy issues. – jtlindsey May 21 '20 at 15:16
  • Nodemailer simply don't work if we pass an array. – SalahAdDin May 26 '22 at 19:51
  • Nodemailer also does not get array as input, it must be a string. – SalahAdDin May 30 '22 at 13:07
15

Your problem is referencing the same msg object from async code. The foreach completes before the sendMail would send out the emails.

So msg.to wil be the last item from the maiilist object.

Try to clone/copy msg inside maillist foreach, or just move msg definition to there :

maillist.forEach(function (to, i , array) {


  var msg = {
        from: "******", // sender address
        subject: "Hello ✔", // Subject line
        text: "Hello This is an auto generated Email for testing  from node please ignore it  ✔", // plaintext body
        cc: "*******"    
        //  html: "<b>Hello world ✔</b>" // html body
    }
  msg.to = to;

  smtpTransport.sendMail(msg, function (err) {
eenagy
  • 912
  • 1
  • 8
  • 22
6
var maillist = [
  '****.sharma3@****.com',
  '****.bussa@****.com',
  '****.gawri@****.com',
];

maillist.toString();

var msg = {
    from: "******", // sender address
    to: maillist,
    subject: "Hello ✔", // Subject line
    text: "Hello This is an auto generated Email for testing  from node please ignore it  ✔", // plaintext body
    cc: "*******"    
    //  html: "<b>Hello world ✔</b>" // html body
}
Scorpio
  • 2,309
  • 1
  • 27
  • 45
Nitin Kumar
  • 348
  • 5
  • 9
6
let info = await transporter.sendMail({
  from: [{ name: "sender Name", address: "sender@example.com" }], // sender address
  to: [
    { name: "Receiver Name 1", address: "receiver1@example.com" },
    { name: "Receiver Name 2", address: "receiver2@example.com" },
  ],
  subject: "Hey you, awesome!",
  html: "<b>This is bold text</b>",
  text: "This is text version!",
});
  • You can send email to multiple recipient by keeping inside the array of objects where name is Receiver name and address is the receiver email address.
  • In Above way I am able to send email to multiple recipient.
Abhishek Kumar
  • 111
  • 1
  • 7
4

A good way to do it asynchronously would be to use the each function in the async module: https://caolan.github.io/async/docs.html#each

var async = require("async");

async.each(maillist, function(to, callback){

    msg.to = to;

    smtpTransport.sendMail(msg, function (err) {
        if (err) { 
            console.log('Sending to ' + to + ' failed: ' + err);
            callback(err);
        } else { 
            console.log('Sent to ' + to);
            callback();
        }
    });
}, function(err){
    if(err){
        console.log("Sending to all emails failed:" + err);
    }

    //Do other stuff or return appropriate value here
});
bmpickford
  • 90
  • 1
  • 9
3

The sendMail method is actually gets resolved after the forEach loop finishes, but the issue is the sendMail method does not return a type of promise, so if you try awaiting this, it still wont work.

so what you need to do is to create a separate function for sendmail therefore making it a promise like this

    const send = (transporter: any, mailOptions: any) => {
    return new Promise((resolve, reject) => {
        transporter.sendMail(mailOptions, (error: any, info: any) => {
          if (error) {
            return reject(error);
          } else {
            return resolve();
          }
        });
    });
    };

so this enables to await this and therefore the iterator will wait for the process to finish before going to the next loop.

The full code should look like this

    let transporter = nodemailer.createTransport({
      host: "mail.smtp.com", // your server host address
      port: 587, // port
      secure: false, // use TLS // true for 465, false for other ports
      auth: {
        user: EMAIL_USER, // your email address
        pass: EMAIL_PSW, // your password
      },
      tls: {
        rejectUnauthorized: false
      }
    });

    // store an array of errors if any
    let successful: any[] = [];
    let failed: any[] = [];
    await recipients.forEach(async (to, i) => {
      let mailOptions = {
        from, // sender address
        to, // list of receivers
        subject, // Subject line
        text // plain text body
      };

      if (html) {
        (mailOptions as any).html = html;
      }

      // send mail with defined transport object
      // here we use the fuction we created which is now a promise
      await send(transporter, mailOptions)
        .then(() => {
          successful.push({ success: true, to });
        })
        .catch(reason => {
          failed.push({ success: false, to, message: reason });
        });
      if (i === recipients.length - 1) {
        if (failed.length === recipients.length) {
          return reject({ failed });
        } else {
          return resolve({ successful, failed });
        }
      }
    });
  });


const send = (transporter: any, mailOptions: any) => {
return new Promise((resolve, reject) => {
    transporter.sendMail(mailOptions, (error: any, info: any) => {
      if (error) {
        return reject(error);
      } else {
        return resolve();
      }
    });
});
};
Klexzi
  • 46
  • 3
0

You are sending the emails asynchronously so you need a waiting function that waits for all the mails till they have been sent because if not, you program exits and some of the requests are not fulfilled. So you have to do sort of a timeout function that checks if the emails have been sent.

satchcoder
  • 797
  • 5
  • 11
-1
maillist.forEach(function (to, i , array) {
  (function(i,to){
     msg.to = to;
  smtpTransport.sendMail(msg, function (err) {
    if (err) { 
      console.log('Sending to ' + to + ' failed: ' + err);
      return;
    } else { 
      console.log('Sent to ' + to);
    }

    if (i === maillist.length - 1) { msg.transport.close(); }
  });
})(i,to)
});
  • From Review:  Hi, please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Feb 05 '20 at 11:07
  • That is not a good solution because it would email separately. A better approach would send emails one single time. – Pierre Aug 27 '22 at 02:28