Nodemailer works locally, but does nothing in prod (No error though)

I have seen other similar posts, but they all seem to be getting an error. I am not getting any error. I have nodemailer set up to send an email when an event is scheduled in a calendar. It works when I run it locally, but in production it does not send the email. I have try catch blocks and console logs in the try blocks and in the catch blocks, and I dont see anything getting logged to the function logs. I added a console log right before the transporter and it does get logged to the function logs, but nothing else after that.

Any help would be much appreciated! My site name is sharp-carson-8083f1.netlify.app/home, and here is the code for it

function sendNewAppointmentEmail(newEvent) {
try {

    const startTime = moment(newEvent.start).format('hh:mm A')
    const endTime = moment(newEvent.end).format('hh:mm A')
    const date = moment(newEvent.start).format('MMM DD YYYY');
    const email = newEvent.email === 'undefined' ? '' : newEvent.email;
    const phoneNumber = newEvent.phoneNumber === 'undefined' ? '' : newEvent.phoneNumber;

  
    const { EMAIL_ADDRESS, EMAIL_USERNAME, EMAIL_PASSWORD, TO_EMAIL_ADDRESS } = process.env;


    const emailData = {
      from: EMAIL_ADDRESS,
      to: TO_EMAIL_ADDRESS,
      subject: 'Capital Cleaners - Nueva Cita',
      text: 'Se fijó una nueva cita en el calendario',
      html: `<p>Nombre: ${newEvent.firstName + ' ' + newEvent.lastName} </p> 
             <br/>
             <p>Fecha: ${date}</p>
             <br/>
             <p>Comienzo: ${startTime}</p>
             <br/>
             <p>Final: ${endTime}</p>
             <br/>
             <p>Email: ${email}</p>
             <br/>
             <p>Phone Number: ${phoneNumber}</p>`,
    };


    console.log({ emailData })  // This does get logged in the function logs

    const transporter = nodemailer.createTransport({
      port: 465,
      host: "smtp.gmail.com",
         auth: {
              user: EMAIL_USERNAME,
              pass: EMAIL_PASSWORD
           },
      secure: true,
    });  

    const res = transporter.sendMail(emailData, (error, info) => {
        try {
            if (error) {
              console.log({ sendEmailError: error })

              return error;
            }

            console.log({ sendEmailRes: info })

            return { message: 'Mail send', message_id: info.messageId };
        }
        catch (transporterError) {
            console.log({ transporterError });

            return transporterError;
        }

    })      

    return res;
}
catch (error) {
    console.log({ sendEmailError })

    return error;
}

}

Thanks in advance!

Hi @paulyg15,

In the past, I had given nodemailer a go and faced the same problem as yours - couldn’t get it to work in production. I switched the library and now it’s working, so I can suggest you that if you’re open to options. I also have a working example ready in that.

The problem I believe is that, in Netlify Functions, you’ve to use a return statement to get async tasks done. For example, in a client-side browser code you can probably do something like:

fetch('url').then(response => response.json()).then(data => console.log(data))

and that would work perfectly. But if you do the same thing in a serverless function, you’d see that that part would probably never execute. So you’ve to do something like:

return fetch('url').then(response => response.json()).then(data => {
  console.log(data)
  return {
    statusCode: 200,
    body: JSON.stringify(data)
  }
})

I’ve not gone too much into why this works, but I assume, it needs a return statement to wait for something to actually execute. Without it, it’d just go on processing. It’s just something where I think “if it works, don’t touch it”.

Now how this relates to Nodemailer? I don’t know. I’ve not checked its docs too much to see if it supports such chaining or if it supports async/await. If it does, you might be in luck.

If not, I personally use emailjs: emailjs - npm.

It works like this:

const SMTPClient = require('emailjs').SMTPClient

exports.handler = async () => {
  return new SMTPClient({
    port: 587,
    tls: true,
    host: '<host>',
    user: '<login>',
    password: '<password>'
  }).sendAsync({
    from: 'Foo Bar <foo.bar@example.com>',
    subject: 'Testing',
    to: 'John Doe <john.doe@example.com>',
    text: '<p>Supports HTML</p>'
  }).then(() => {
    return {
      statusCode: 200,
      body: JSON.stringify(true)
    }
  })
}

Ah yes, Nodemailer does support async/await as per this example on their site.

I have a (far from elegant) script I cobbled together to test with VueJS and Ethereal Email if it is of any use.