Search code examples
node.jsexpressnodemailer

Send response after email send node express


I have the following method for /signup route:

const {sendEmail} = require("../lib/email");

exports.signup = async (req, res, next) => {
  try {
       //validate request and save user in DB
    
        let content = {
          activateLink: "link to activate email",
        };

      await sendEmail(email, "Verification OTP", "otp", content, next);

      res.status(201).json({
       message: "User created successfully !",
       userData: {
       _id: result._id.toString(),
        email: result.email,
       },
      });
  }
    catch (err) {
        if (!err.statusCode) {
          err.statusCode = 500;
        }
        next(err);
  }
};

Method for sending email:

const path = require("path");
const nodemailer = require("nodemailer");
const hbs = require("nodemailer-express-handlebars");
const viewPath = path.resolve(__dirname, "../templates/views");
const partialsPath = path.resolve(__dirname, "../templates/partials");
const config = require("../config");

exports.sendEmail = async (to, subject, viewName, content, next) => {
    try {
      const transporter = nodemailer.createTransport(config.mailConfig);
      const handlebarsOptions = {
        viewEngine: {
          extName: ".handlebars",
          layoutsDir: viewPath,
          defaultLayout: false,
          partialsDir: partialsPath,
        },
        viewPath: viewPath,
        extName: ".handlebars",
      };
  
      transporter.use("compile", hbs(handlebarsOptions));
  
      const mailOptions = {
        from: "noreply@admin.com", // Update from email
        to: to,
        subject: subject,
        template: viewName,
        context: content,
      };
  
      let info = await transporter.sendMail(mailOptions);
      console.log("Message sent: %s", info.messageId);
    } catch (e) {
      next(e);
    }
  };

With the above code blocks. I'm able to send email to newly registered user when I send post request. But postman keeps sending request and doesn't get any response status and message after email is sent.

I think code execution is not getting out of sendEmail method. Adding return true; after console.log("Message sent: %s", info.response); is also not helping at all.

I would be thankful if anyone could help me to figure out the solution.


Solution

  • LATEST EDIT In the comments you mention it is middleware, which would be correct with the function signature of (req, res, next).

    But when I look at the line you posted in the comments router.post("/user/signup",UserController.signup); it is a normal route where the signature should be (req, res).

    So could you try:

    const {sendEmail} = require("../lib/email");
    
    exports.signup = async (req, res) => {
      try {
           //validate request and save user in DB
    
            let content = {
              activateLink: "link to activate email",
            };
    
          const result = await sendEmail(email, "Verification OTP", "otp", content);
    
          res.status(201).json({
           message: "User created successfully !",
           userData: {
           _id: result._id.toString(),
            email: result.email,
           },
          });
      } 
      catch (err) {
        console.log(`Something went wrong sending activation mail`, err);
        throw new Error(err);
      }
    };
    
    import { rejects } from "assert";
    
    exports.sendEmail = async (to, subject, viewName, content) => {
    
        return new Promise((resolve, reject) => {
          const transporter = nodemailer.createTransport(config.mailConfig);
          const handlebarsOptions = {
            viewEngine: {
              extName: ".handlebars",
              layoutsDir: viewPath,
              defaultLayout: false,
              partialsDir: partialsPath,
            },
            viewPath: viewPath,
            extName: ".handlebars",
          };
    
          transporter.use("compile", hbs(handlebarsOptions));
    
          const mailOptions = {
            from: "noreply@admin.com", // Update from email
            to: to,
            subject: subject,
            template: viewName,
            context: content,
          };
    
          let info = transporter.sendMail(mailOptions, (err, result) => {
                if (err) {
                    reject(err);
                    return;
                }
                console.log("Message sent: %s", result.messageId);
                resolve(result);
           });
        });
    };
    

    Which version of nodemailer are you using? Pre 6.4.1 does not return a Promise so you need to use a callback then.

    With callback the code will look something like this:

    const {sendEmail} = require("../lib/email");
    
    exports.signup = async (req, res, next) => {
      try {
           //validate request and save user in DB
        
            let content = {
              activateLink: "link to activate email",
            };
    
          const result = await sendEmail(email, "Verification OTP", "otp", content, next);
    
          res.status(201).json({
           message: "User created successfully !",
           userData: {
           _id: result._id.toString(),
            email: result.email,
           },
          });
      }
        catch (err) {
            if (!err.statusCode) {
              err.statusCode = 500;
            }
            next(err);
      }
    };
    
    import { rejects } from "assert";
    
    exports.sendEmail = async (to, subject, viewName, content) => {
    
        return new Promise((resolve, reject) => {
          const transporter = nodemailer.createTransport(config.mailConfig);
          const handlebarsOptions = {
            viewEngine: {
              extName: ".handlebars",
              layoutsDir: viewPath,
              defaultLayout: false,
              partialsDir: partialsPath,
            },
            viewPath: viewPath,
            extName: ".handlebars",
          };
      
          transporter.use("compile", hbs(handlebarsOptions));
      
          const mailOptions = {
            from: "noreply@admin.com", // Update from email
            to: to,
            subject: subject,
            template: viewName,
            context: content,
          };
      
          let info = transporter.sendMail(mailOptions, (err, result) => {
                if (err) {
                    reject(err);
                    return;
                }
                console.log("Message sent: %s", result.messageId);
                resolve(result);
           });
        });
    };
    

    And you need to return the result (info) from let info = await transporter.sendMail(mailOptions); to the signup method.

            _id: result._id.toString(), <-- result will be undefined here
            email: result.email,        <-- result will be undefined here