Send response after email send node express

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: "[email protected]", // 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: "[email protected]", // 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