NodeJS sending e-mails with a delay NodeJS sending e-mails with a delay express express

NodeJS sending e-mails with a delay


Well some folks may come here and tell you to use an external queue system and bla bla... But you could simply use plain old Javascript to schedule the sending 20*60*1000 milliseconds into the future to get things started. :)

There's however a problem with your code: you're waiting for the mailer to succeed before sending the 200 - 'Message sent' response to the user. Call me a madman but I'm pretty sure the user won't be staring at the browser window for 20 minutes, so you'll probably have to answer as soon as possible and then schedule the mail. Modifying your code:

router.post('/', (req, res) => {    const DELAY = 20*60*1000 // min * secs * milliseconds    const transporter = nodemailer.createTransport(smtpTransport({      host: 'smtp.gmail.com',      port: 465,      auth: {          user: 'noreply@domain.nl',          pass: 'pass123'      }    }));    const mailOptions = {      from: `"${req.body.name}" <${req.body.email}>`,      to: 'info@domain.nl',      subject: 'Form send',      html: `Content`    };    res.status(200).json({ responseText: 'Message queued for delivery' });    setTimeout(function(){      transporter.sendMail(mailOptions, (error, info) => {        if (error)           console.log('Mail failed!! :(')        else          console.log('Mail sent to ' + mailOptions.to)      }),      DELAY    );  }}); 

There are however many possible flaws to this solution. If you're expecting big traffic on that endpoint you could end up with many scheduled callbacks that will eat the stack. In addition, if something fails the user of course won't be able to know.

If this is a big / serious project, consider using that cronjob package or using an external storage mechanism where you can queue this "pending" messages (Redis would do and it's incredible simple), and have a different process read tasks from there and perform the email sending.

EDIT: saw some more things on your code.

1) You probably don't need to create a new transport inside your POST handler, create it outside and reuse it.

2) In addition to the mentioned problems, if your server crashed no email will be ever sent.

3) If you still want to do it in a single Node.js app, instead of scheduling an email on every request to this endpoint, you'd be better storing the email data (from, to, subject, body) somewhere and schedule every 20 minutes a function that will get all pending emails, send them one by one, and then reschedule itself to re-run 20 minutes later. This will keep you memory usage low. Server crash still make all emails lost, but if you add REDIS into the mix then you can simply grab all pending emails from REDIS when your app start.

Probably too much for an answer, sorry if it wasn't needed! :)


I think CharlieBrown's answer is correct and since I had two answers in my mind while reading the question, I thank him for simplifying my answer to be the alternative of his.

setTimeout is actually a good idea, but it has a drawback: in the case when there is any reason to stop the server code (server restart, module installation, file management, etc.) your callbacks scheduled at the end of the setTimeout's time parameter will not be executed and some users will not receive emails.

If the problem above is serious-enough, then you might want to store scheduled emails to be sent in the database or into Redis and use a cron job to periodically check the email set and send the emails if there are some.

I think that either this answer or CharlieBrown's should suffice for you, depending on your preferences and needs.