I have a route that allows a user to reset their password by sending them an email. Standard procedure for most websites. In this route, I import the async npm module and use the waterfall method so that I can handle the asynchronous nature of multiple functions. I'm still having a bit of trouble understanding promises, but I'm trying to replace waterfall with a promise or promise chain.
How could I refactor this route with a promise? Here are the steps contained in this route that is currently split up into 4 functions with waterfall.
Return a status of 200.
app.post('/forgotPassword', function(req, res, next) {
waterfall([
// generate reset token
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
// search for user with the given email
User.findOne({ email: req.body.email }, function(err, user) {
// check to see if the user exists
if (!user) {
// user doesn't exist in database
return res.status(404).send();
}
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'SendGrid',
auth: {
user: config.sendgridUser,
pass: config.sendgridPassword
}
});
var mailOptions = {
to: user.email,
from: '[email protected]',
subject: 'Password Reset',
text: `Hello etc etc`,
smtpTransport.sendMail(mailOptions, function(err) {
done(err, 'done');
});
}],
function(err) {
// handle error
if (err) return next(err);
res.status(200).send();
});
}); // end POST route '/forgotPassword'
Promise
is a very powerful tool. It can be hard to understand it at the beginning, but totally worth the effort! Please let me know if you have any doubts :)
app.post('/forgotPassword', function(req, res, next)
{
new Promise((resolve, reject) =>
{
// generate reset token
crypto.randomBytes(20, (err, buf) =>
{
if(err)
return reject(err);
const token = buf.toString('hex');
resolve(token);
});
})
.then((token) =>
{
return new Promise((resolve, reject) => {
// search for user with the given email
User.findOne({ email: req.body.email }, (err, user) =>
{
if (!user)
return reject(404);
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
user.save(function(err) {
if(err)
return reject(err);
resolve(user);
});
});
});
})
.then((user) =>
{
return new Promise((resolve, reject) =>
{
const smtpTransport = nodemailer.createTransport('SMTP',
{
service: 'SendGrid',
auth: {
user: config.sendgridUser,
pass: config.sendgridPassword
}
});
const mailOptions = {
to: user.email,
from: '[email protected]',
subject: 'Password Reset',
text: `Hello etc etc`
};
smtpTransport.sendMail(mailOptions, (err) =>
{
if(err)
return reject(err);
resolve();
});
});
})
.then(() => res.sendStatus(200))
.catch((err) =>
{
//check if the error is the one from the DB where the user was not found
if(err == 404) {
return res.sendStatus(404);
}
return res.status(500).send(err);
});
});