Netlify function for sending mail with nodemailer doesn't work in production

Greetings,

I have encountered a problem with netlify functions, specifically, I’m using nodemailer package to send an email from a website. Everything works fine in development and I’m able to receive mail from the site, but in a production environment, I checked the Network tab and it showing Status code 200, but the response body is empty when it should display the message “Message successfully sent” and the message doesn’t arrive in the inbox.

In netlify i checked the Function log and it displays payload with data from a form.

ContactForm.js

<Formik
              onSubmit={async (values, actions) => {
                  const options = {
                     method: 'POST',
                     body: JSON.stringify(values),
                    headers: {
                        'Content-Type': 'application/json'
                   }

               };

              await fetch('/.netlify/functions/sendmail', options)
              .then((res) => {

                actions.setSubmitting(false);
                if (res.ok) setIsSubmited(true);

         })
       catch((err) => {
            actions.setSubmitting(false);
      });
 }}
>

sendmail.js

exports.handler = async (event, context, callback) => {

   const payload = JSON.parse(event.body);

    const msg = {
        to: process.env.MAIL_USERNAME,
        from: payload.email,
        subject: `New message from ${payload.name} | ${payload.email}`,
        text: payload.message
    };

    const transporter = nodemailer.createTransport({

        host: process.env.MAIL_HOST,
        port: 587,
        secure: false,

        auth: {
            user: process.env.MAIL_USERNAME,
            pass: process.env.MAIL_PASSWORD
        },

        tls: {
            rejectUnauthorized: false
        }

    });

    try {
        await transporter.sendMail(msg, (error) => {
            if (!error) {
                callback(null, { statusCode: 200, body: 'Message successfully sent' });
            } else {
                callback(null, { statusCode: 400, body: error });
            }

        });

    } catch (e) {

        callback({ statusCode: e.code, body: e });
    }

};

Please let me know if any other information needs in order to help solve my problem.
Thank you.

Welcome to Netlify @reginatrade

Using a combination of your code and the example from Nodemailer/Ethereal Email I have this working example

const nodemailer = require("nodemailer");

exports.handler = async (event, context, callback) => {

    const transporter = nodemailer.createTransport({
        host: process.env.MAIL_HOST,
        port: 587,
        secure: false,
        auth: {
            user: process.env.MAIL_USERNAME,
            pass: process.env.MAIL_PASSWORD
        }
    });

    let info = await transporter.sendMail({
        from: `"My Name 👻" <${process.env.MAIL_USERNAME}>`,
        to: "bar@example.com, baz@example.com",
        subject: "Hello ✔",
        text: "Hello world?",
        html: "<b>Hello world?</b>",
    });

    if (info.messageId) {
        return {
            statusCode: 200,
            body: nodemailer.getTestMessageUrl(info)
        }
    }
  
    return {
        statusCode: 400,
        body: "Oops"
    }
};
2 Likes

Hey, thank you for providing an example, but unfortunately, it doesn’t work in functions logs I’m getting this error

ERROR Invoke Error {"errorType":"Error","errorMessage":"Missing credentials for \"PLAIN\"","code":"EAUTH","command":"API","stack":["Error: Missing credentials for \"PLAIN\""," at t.exports._formatError (/var/task/sendmail.js:1:25819)"," at t.exports.login (/var/task/sendmail.js:1:21038)"," at /var/task/sendmail.js:1:191929"," at t.exports.<anonymous> (/var/task/sendmail.js:1:17645)"," at Object.onceWrapper (events.js:420:28)"," at t.exports.emit (events.js:314:20)"," at t.exports._actionEHLO (/var/task/sendmail.js:1:33454)"," at t.exports._processResponse (/var/task/sendmail.js:1:27841)"," at t.exports._onData (/var/task/sendmail.js:1:25511)"," at TLSSocket._onSocketData (/var/task/sendmail.js:1:17335)"]}

and response status is 500, with my provided example I was able to get status code 200, but the mail didn’t arrive

As well I need to add this parameter

tls: {
    rejectUnauthorized: false
}

otherwise, I’m getting a certificate mismatch error.

Tested in dev and its works but not in production

Have you included your credentials? MAIL_USERNAME (and other variables) need setting prior to the function build (see Build environment variables | Netlify Docs)

Is it possible your mail system (receiving) flagged the message as spam, or outright rejected it? Do you have SPF/DKIM set up on your outgoing mail address? The reason I used Ethereal Email is because if I used my normal email, I would need to add a raft settings to ensure it didn’t bounce.

Then all you need do is add that in. I did not require it for my testing so omitted it.

I tested this in both without issue.

Thank you soo much for your help, the problem actually got resolved by changing port to 465 and then I removed this line

tls: {
    rejectUnauthorized: false
}

And everything works now.

Thanks once again!

2 Likes

Hey there, @reginatrade :wave:

Thank you so much for coming back and sharing this update with the Forums :netliconfetti: This will definitely be beneficial to members who encounter something similar in the future.

I have a similar issue with nodemailer only not working in prod. I can see the function is called but the email is not sent when deployed in prod.

const nodemailer = require('nodemailer');

exports.handler = async (event) => {
    const form = JSON.parse(event.body);

    // Use Smtp Protocol to send Email
    const smtpTransport = nodemailer.createTransport({
        host: 'smtp.gmail.com',
        port: 465,
        secure: true,
        auth: {
            type: 'oauth2',
            user: process.env.EMAIL, // generated ethereal user
            pass: process.env.APP_PW, // generated ethereal password
            clientId: process.env.OAUTH_CLIENTID,
            clientSecret: process.env.OAUTH_CLIENT_SECRET,
            refreshToken: process.env.OAUTH_REFRESH_TOKEN,
            accessToken: process.env.OAUTH_ACCESS_TOKEN,
        },
        logger: true,
    });

    const mail = {
        from: 'email1@mail.com',
        to: 'email2@mail.com',
        replyTo: `${form.email}`,
        subject: `subject text`,
        html: `<html>
        <head>
        <title>Website Lead Email</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        </head>
        <body style="font-size: 1.2rem;">
        test message

        </body>
        </html>
        `,
    };
    try {
        console.log('attempting to send email');
        smtpTransport.sendMail(mail, function (error, response) {
            if (error) {
                console.log(error);
            } else {
                console.log('Message sent!', response);
            }
            smtpTransport.close();
        });
        return {
            statusCode: 200,
            body: JSON.stringify('success email req received'),
        };
    } catch (err) {
        console.error(err);
        return {
            statusCode: 500,
            body: JSON.stringify('something went wrong'),
        };
    }
};

I’ve never got nodemailer working in production, so I simply switched to another library and it’s been working great:

I’ve had Nodemailer running in production on multiple occasions @techx-guy without issue (see sample code here.) I’ve not used it with OAuth2 for Gmail though, but more than likely it is the options used that are causing the issue.

Hey thanks @coelmay that solved my problem. I also ended up using zoho email and it worked like a charm in prod. Thank so much.

2 Likes