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: "[email protected]", // 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.
CodePudding user response:
Your code should work fine. Since we're missing some of the code, I can only think one of the following may have happened:
- Express failed stringifying result.email (Circular JSON)
- The transporter.sendMail() promise does not resolve (Does the "Message sent:..." log show?)
Can you post console logs?
Regardless, I would suggest separating your sendEmail function from the responsibility of catching and passing the error to the next function, since the whole function is surrounded with one try catch block, you may as well let the sendEmail throw and catch it as you're already doing. Now sendEmail doesn't have to be called by an express route / middleware
CodePudding user response:
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