'mongoose middleware pre update
I am using
schema.pre('save', function (next) {
if (this.isModified('field')) {
//do something
}
});
but I now need to use this same function isModified
in a schema.pre('update'
hook, but it does not exists. Does anyone know how I can use this same functionality in the update
hook?
Solution 1:[1]
Not possible according to this:
Query middleware differs from document middleware in a subtle but important way: in document middleware, this refers to the document being updated. In query middleware, mongoose doesn't necessarily have a reference to the document being updated, so this refers to the query object rather than the document being updated.
update
is query middleware and this
refers to a query object which has no isModified
method.
Solution 2:[2]
@Jeremy I've arrived to the same issue and finally got a workaround:
schema.pre('update', function(next) {
const modifiedField = this.getUpdate().$set.field;
if (!modifiedField) {
return next();
}
try {
const newFiedValue = // do whatever...
this.getUpdate().$set.field = newFieldValue;
next();
} catch (error) {
return next(error);
}
});
Taken from here: https://github.com/Automattic/mongoose/issues/4575
With this, you can check if it comes any update on a field but you can't check if the incoming value is different than stored. It works perfect for my use case (encrypt password after reset)
I hope it helps.
Solution 3:[3]
Schema.pre('updateOne', function (next) {
const data = this.getUpdate()
data.password = 'Teste Middleware'
this.update({}, data).exec()
next()
})
const user = await User.updateOne({ _id: req.params.id }, req.body)
this worked to me
Solution 4:[4]
Not quite a solution for OP, but this is what worked for me Best solution I tried, taken from here
schema.pre("update", function(next) {
const password = this.getUpdate().$set.password;
if (!password) {
return next();
}
try {
const salt = Bcrypt.genSaltSync();
const hash = Bcrypt.hashSync(password, salt);
this.getUpdate().$set.password = hash;
next();
} catch (error) {
return next(error);
}
});
Solution 5:[5]
Actually André Rodrigues Answer was almost perfect, but in Mongoose v5.13.0 you can easily mutate the body itself without executing it by
schema.pre('updateOne', async function () {
let data = this.getUpdate();
const salt = await bcrypt.genSalt();
data.password = await bcrypt.hash(data.password, salt);
});
Welcome as always :)
Solution 6:[6]
Well, what i have done in case of password hashing in update method is:
userRouter.patch("/api/users/:id", async (req, res) => {
const updatedAttributes = Object.keys(req.body);
const availableUpates = ["firstName", "lastName", "email", "password"];
const check = updatedAttributes.every((udate) =>
availableUpates.includes(udate)
);
if (!check) {
return res.status(400).send("Requested Fields Can't Be Updated");
}
const password = req.body.password;
if (password) {
const hashedPass = await bcrypt.hash(password, 8);
req.body.password = hashedPass;
}
try {
const user = await User.findByIdAndUpdate(req.params.id, req.body, {
runValidators: true,
new: true,
});
res.status(202).send(user);
} catch (error) {
res.status(400).send(error);
}
});
And don't forget to import needed npm modules.
Solution 7:[7]
//Wouldn't work for $inc updates etc...
Schema.pre("updateOne", function(next){
const data = this.getUpdate()
let updateKeys = Object.keys(data)
let queryArr = []
const checkQuery = (arr, innerData = data) => {
arr.map((elem) => {
if (elem === "$set" || elem === "$push") {
checkQuery(Object.keys(innerData[elem]), innerData[elem]);
} else {
queryArr.push(elem);
}
})
};
checkQuery(updateKeys);
const isModified = (value) => queryArr.includes(value)
if(isModified("field")) {
data.field = whatever...
}
next()
})
I recursively checked for keys here
Solution 8:[8]
This was my solution, using bcrypt and an async function. One of the big points of confusion for me is that changes are passed under this._update which differs from handling on .pre('save') where they're passed directly. So you see I had to call this._update.password as opposed to simply this.password.
UserSchema.pre('findOneAndUpdate', async function() {
const userToUpdate = await this.model.findOne(this.getQuery())
if (userToUpdate.password !== this._update.password) {
this._update.password = await bcrypt.hash(this._update.password, 12)
}
})
Solution 9:[9]
This worked for me to change the user password
userSchema.pre("updateOne", function (next) {
const user = this;
if (user.getUpdate().password !== undefined) {
bcrypt.hash(user.getUpdate().password, 10, (err, hash) => {
if (err) return next(err);
user.getUpdate().password = hash;
return next();
});
} else {
return next();
}
});
Solution 10:[10]
But you can use query hooks; although you might need to use POST, rather than PRE hooks.
schema.pre('findOneAndUpdate', async function() {
const docToUpdate = await this.model.findOne(this.getQuery());
console.log(docToUpdate); // The document that `findOneAndUpdate()` will modify
});
So,
schema.post(/update/i, async (query, next) {
if (query instanceof mongoose.Query) {
await Model.find(query.getQuery()).each((el) => {
if (isModified('field')) {
//do something
}
})
}
next()
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow