What I did wrong here? I receive "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client NodeJS", can somebody help me?
I am using:
Express mongoose (for MongoDB)
Controller
export const postResetPasswordEmail: Controller = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const { email } = req.body
try {
const errors: IValidatorError = getValidationErrors(req)
if (Object.keys(errors).length !== 0) {
res.status(403).json({ errors })
return
}
const user = await User.findOne({ email })
if (!user) {
res.status(404).json({ message: AuthenticationErrors.noEmailFound })
return
} else {
if (!(user.passwordUpdatedAt === undefined || (new Date(user.passwordUpdatedAt) < new Date()))) {
res.status(404).json({ message: AuthenticationErrors.error10MinutesPassword })
return
}
}
const token = generateJWT({ email }, JWTKEY, '10m')
await sendResetPasswordMail(email, token, next)
res.status(200)
.json({ message: SuccessMessages.resetPassword })
} catch (err: any) {
catchError(err, next)
}
}
sendResetPasswordMail
const sendResetPasswordMail: SendResetPasswordMail = async (userEmail: string, token: string, next: NextFunction): Promise<void> => {
try {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: adminEmail.email,
pass: adminEmail.pass
}
})
const mailOptions = {
from: adminEmail.email,
to: userEmail,
subject: 'Reset password',
text: `Reset password: http://localhost:3000/resetPassword?token=${token}`
}
await transporter.sendMail(mailOptions)
} catch (err: any) {
catchError(err, next)
}
}
generateJWT
const generateJWT: GenerateJWT = (userInfo: IUserInfo, JWTKEY: string, expireTime: string): string =>
jwt.sign({ ...userInfo }, JWTKEY, { expiresIn: expireTime })
I see a flaw in sendResetPasswordMail: Suppose you have an exception there and it calls catchError within sendResetPasswordMail catch block, in this case it will indirectly call next, which in turn would call res.status().send() within default express error handler.
Nevertheless, despite the exception sendResetPasswordMail will return control to you controller which would try to send status 200 after that, and if by that time the error handler's already sent the response you will get the error.
The valid behaviour would be not to catch exceptions within sendResetPasswordMail and leave it to the caller. And you also can remove next parameter from sendResetPasswordMail