Reputation: 2853
I have this code
var ClientSchema = new Schema({
name: {type: String, required: true, trim: true}
});
var Client = mongoose.model('Client', ClientSchema);
Using express, I create a new client with this code
var client = new Client(req.body);
client.save(function(err, data) {
....
});
If I leave the name field empty on the form, mongoose doesn't allow to create the client because I set it as required on the schema. Also, if I leave spaces before and after the name, mongoose delete that spaces before save.
Now, I try to update a client with this code
var id = req.params.id;
var client = req.body;
Client.update({_id: id}, client, function(err) {
....
});
It let me to change the name, but if I leave it empty on the form, mongoose doesn't validate and save an empty name. If I add empty spaces before and after the name, it save the name with spaces.
Why mongoose validate on save but not on update? I'm doing it in the wrong way?
mongodb: 2.4.0 mongoose: 3.6.0 express: 3.1.0 node: 0.10.1
Upvotes: 78
Views: 40762
Reputation: 4295
The accepted answer does not work if you use upsert
in the findOneAndUpdate
options. The way around this is to create a model static method that does a findOne
and then updateOne
or create
under the hood. create
runs validation automatically.
export async function findOneAndUpdateWithValidation(
this: LocationModel,
filter: FilterQuery<LocationDocument>,
update: UpdateQuery<LocationDocument>,
options?: QueryOptions
) {
const documentFound = await this.findOne(filter);
if (!documentFound) return this.create(update);
return this.updateOne(filter, update, options);
}
locationSchema.statics = {
findOneAndUpdateWithValidation
}
Upvotes: 2
Reputation: 5020
You can run validation while update by setting the option runValidators: true
.
const Kitten = db.model('Kitten', kittenSchema);
const update = { color: 'blue' };
const opts = { runValidators: true };
Kitten.updateOne({}, update, opts, function() {
// code
});
const Kitten = db.model('Kitten', kittenSchema);
const update = { color: 'blue' };
const opts = { runValidators: true };
Kitten.updateOne(
{
_id: req.params.id
},
{
$set: { ...update },
},
opts
).then(result => {
// code
})
Read More: https://mongoosejs.com/docs/validation.html#update-validators
Upvotes: 3
Reputation: 3536
MongoDB does not run validation on updates by default.
in order to make validation works by default when an update happens, just before connecting to MongoDB you can set global setting only ones like that:
mongoose.set('runValidators', true); // here is your global setting
mongoose.connect(config.database, { useNewUrlParser: true });
mongoose.connection.once('open', () => {
console.log('Connection has been made, start making fireworks...');
}).on('error', function (error) {
console.log('Connection error:', error);
});
So any built-in or custom validation will run on any update as well
Upvotes: 17
Reputation: 14552
As of Mongoose 4.0 you can run validators on update()
and findOneAndUpdate()
using the new flag runValidators: true
.
Mongoose 4.0 introduces an option to run validators on
update()
andfindOneAndUpdate()
calls. Turning this option on will run validators for all fields that yourupdate()
call tries to$set
or$unset
.
For example, given OP's Schema:
const ClientSchema = new Schema({
name: {type: String, required: true, trim: true}
});
const Client = mongoose.model('Client', ClientSchema);
Passing the flag on each update
You can use the new flag like this:
const id = req.params.id;
const client = req.body;
Client.update({_id: id}, client, { runValidators: true }, function(err) {
....
});
Using the flag on a pre
hook
If you don't want to set the flag every time you update something, you can set a pre
hook for findOneAndUpdate()
:
// Pre hook for `findOneAndUpdate`
ClientSchema.pre('findOneAndUpdate', function(next) {
this.options.runValidators = true;
next();
});
Then you can update()
using the validators without passing the runValidators
flag every time.
Upvotes: 116
Reputation: 121
If you add this option in your config of mongoose it works:
mongoose.set('runValidators', true)
Upvotes: 0
Reputation: 79
In your model, ex. Category.js file:
const CategorySchema = mongoose.Schema({
category_name : {
type : String,
required : [true, 'Category Name Is Required !'],
trim : true,
maxlength : [30, 'Category Name Is To Long !'],
unique : true,
});
const Category = module.exports = mongoose.model("Category",CategorySchema);
In your route file:
router.put("/",(req,res,next)=>{
Category.findOneAndUpdate(
{_id : req.body.categoryId},
{$set : {category_name : req.body.category_name} },
**{runValidators: true}**, function(err,result) {
if(err){
if(err.code === 11000){
var duplicateValue = err.message.match(/".*"/);
res.status(200).json({"defaultError":duplicateValue[0]+" Is Already Exsist !"});
}else{
res.status(200).json({"error":err.message} || {"defaultError":'Error But Not Understood !'});
}
}else{
console.log("From category.js (Route File) = "+result);
res.status(200).json({"success":"Category Updated Successfully!!"});
}
});
Upvotes: -1
Reputation: 311835
You're not doing anything wrong, validation
is implemented as internal middleware within Mongoose and middleware doesn't get executed during an update
as that's basically a pass-through to the native driver.
If you want your client update validated you'll need to find
the object to update, apply the new property values to it (see underscore's extend
method), and then call save
on it.
Mongoose 4.0 Update
As noted in the comments and victorkohl's answer, Mongoose now support the validation of the fields of $set
and $unset
operators when you include the runValidators: true
option in the update
call.
Upvotes: 79