A Zarqam
A Zarqam

Reputation: 428

nodemailer sendEmail doesn't wait in Node.js

When I make a request to my endpoint I need to get a successful response only if the email is sent! otherwise, it should throw an error:

myendpoint.js

    router.post("/", upload.none(), async (req, res) => {
    try {
    let body = JSON.parse(req.body.contact);

    await getDb("messages").insertOne({
      name: body.name,
      email: body.email,
      phone: body.phone,
      subject: body.subject,
      message: body.message,
    });

    await sendEmail(body);

    res.send(
      JSON.stringify({
        success: true,
        msg: "Message has been sent successfully",
      })
    );
  } catch (err) {
    res.send(JSON.stringify({ success: false, msg: err }));
  }
});

sendEmail.js

    const sendEmail = async function (props) {
    const transporter = nodemailer.createTransport({
    service: process.env.EMAIL_SERVICE,
    host: process.env.EMAIL_HOST,
    auth: {
      user: process.env.EMAIL_FROM,
      pass: process.env.EMAIL_PASS,
    },
  });
  const mailOptions = {
    from: process.env.EMAIL_FROM,
    to: process.env.EMAIL_TO,
    name: props.name,
    email: props.email,
    phone: props.phone,
    subject: props.subject,
    text: props.message,
  };
  transporter.sendMail(mailOptions, function (error, info) {
    if (error) {
      throw new Error("Message did Not send!");
    }
  });
};

the problem is before "await sendEmail(body);" ends and i get the error, i get the "Message has been sent successfully" and then server crashes! what am i missing?

Upvotes: 1

Views: 3218

Answers (3)

Chukwuemeka Maduekwe
Chukwuemeka Maduekwe

Reputation: 8546

const handler = async ({ subject, name, body, contact }) => {
  const transporter = nodemailer.createTransport({
    service: "zoho",
    // Disable MFA here to prevent authentication failed: https://accounts.zoho.com/home#multiTFA/modes
    // ********************* OR *****************
    // set up Application-Specific Passwords here: https://accounts.zoho.com/home#security/device_logins
    auth: { user: process.env.NEXT_PUBLIC_EMAIL_ADDRESS, pass: process.env.NEXT_PUBLIC_EMAIL_PASSWORD },
  });

  return transporter
    .sendMail(mailOptions({ subject, name, body, contact }))
    .then((info) => {
      if (process.env.NODE_ENV !== "production") console.log("Email sent: " + info.response);
      return true;
    })
    .catch((err) => {
      if (process.env.NODE_ENV !== "production") console.log("error sending mail", err);
      return false;
    });
};

Upvotes: 2

Omar Berrami
Omar Berrami

Reputation: 177

Hello you can change your Transporter sendMail to :

return await transporter.sendMail(mailOptions);

Or You can Use Promise.

Upvotes: 1

Chuong Tran
Chuong Tran

Reputation: 3431

Check document function sendMail from nodemailer at here

If callback argument is not set then the method returns a Promise object. Nodemailer itself does not use Promises internally but it wraps the return into a Promise for convenience.

const sendEmail = async function (props) {
    const transporter = nodemailer.createTransport({
        service: process.env.EMAIL_SERVICE,
        host: process.env.EMAIL_HOST,
        auth: {
            user: process.env.EMAIL_FROM,
            pass: process.env.EMAIL_PASS,
        },
    });
    const mailOptions = {
        from: process.env.EMAIL_FROM,
        to: process.env.EMAIL_TO,
        name: props.name,
        email: props.email,
        phone: props.phone,
        subject: props.subject,
        text: props.message,
    };
    // remove callback and function sendMail will return a Promise
    return transporter.sendMail(mailOptions);
};

Upvotes: 2

Related Questions