Calling an async function in a pre save hook is returning me undefined
for the password. Am I fundamentally misunderstanding async
here? I've used it successfully in other areas of my app an it seems to be working fine...
userSchema.pre('save', function (next) {
let user = this
const saltRounds = 10;
const password = hashPassword(user)
user.password = password;
next();
})
async hashPassword(user) {
let newHash = await bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
console.log(err)
}
return hash
});
return newHash
}
Calling an async function in a pre save hook is returning me undefined
for the password. Am I fundamentally misunderstanding async
here? I've used it successfully in other areas of my app an it seems to be working fine...
userSchema.pre('save', function (next) {
let user = this
const saltRounds = 10;
const password = hashPassword(user)
user.password = password;
next();
})
async hashPassword(user) {
let newHash = await bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
console.log(err)
}
return hash
});
return newHash
}
Share
Improve this question
asked Feb 15, 2018 at 3:17
ModermoModermo
1,9922 gold badges31 silver badges48 bronze badges
6
-
1
do you understand that
const password = hashPassword(user)
will be a Promise - because that's whatasync
functions return (immediately) - therefore, you'd be settinguser.password = a_promise
and callingnext
before the promise has resolved - what the next issue will be, does pre save hook understand promises – Jaromanda X Commented Feb 15, 2018 at 3:28 -
In which case, I'd need to use a
password.then(user.password = password)
? Which makes sense, but I think I need to better my understanding of async/promises – Modermo Commented Feb 15, 2018 at 3:35 -
2
indeed, and call next inside .then ... you could avoid .then by making
userSchema.pre('save', async function (next) {
- then you canconst password = await hashPassword(user)
- functionally, this is (almost0 identical to the .then pattern – Jaromanda X Commented Feb 15, 2018 at 3:36 - Got it. Makes sense now. – Modermo Commented Feb 15, 2018 at 3:37
-
of course, other errors in your code include: 1 -
async hashPassword(user) {
- but user is never used in that function. 2 - where does that function getsaltRounds
from? 3 - the callback to .hash will return hash regardless if there's an error or not – Jaromanda X Commented Feb 15, 2018 at 3:38
3 Answers
Reset to default 4I think you'd need to handle the promise returned by hashPassword:
hashPassword(user)
.then(password => {user.password = password
next()}
)
I don't think you can turn userSchema.pre into an async function.
Mongoose hooks allow async functions in them. It worked for me. Note that the "next" callback parameter is not needed in async functions as the function executes synchronously.
Here is what a correct async/await version of the code posted in the question would be. Please note that the corrections are marked in bold:
userSchema.pre('save', async function () {
let user = this;
const saltRounds = 10;
const hashed_password = await hashPassword(user.password, saltRounds);
user.password = hashed_password;
}); // pre save hook ends here
async hashPassword(password, saltRounds) {
try {
let newHash = await bcrypt.hash(password, saltRounds);
} catch(err){
// error handling here
}
return newHash;
}
Just to clean things up a bit:
userSchema.pre('save', function(next) {
if (!this.isModified('password')) {
return next();
}
this.hashPassword(this.password)
.then((password) => {
this.password = password;
next();
});
});
userSchema.methods = {
hashPassword(password) {
return bcrypt.hash(password, 10);
},
}
let user = this
can be dropped when using an arrow function inthen
.- When using
bcrypt.hash()
with no callback, it returns a promise. - async for hashPassword is redundant when using .then when calling