Reputation: 437
Is there any way to set a field with an "unmodifiable" setting (Such as type, required, etc.) when you define a new Mongoose Schema? This means that once a new document is created, this field can't be changed.
For example, something like this:
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unmodifiable: true
}
})
Upvotes: 12
Views: 10560
Reputation: 3062
From version 5.6.0 of Mongoose, we can use immutable: true
in schemas (exactly as the aforementioned answer on mongoose-immutable
package). Typical use case is for timestamps, but in your case, with username
it goes like this:
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
immutable: true
}
});
If you try to update the field, modification will be ignored by Mongoose.
Going a little further than what have been asked by OP, now with Mongoose 5.7.0 we can conditionally set the immutable
property.
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
immutable: doc => doc.role !== 'ADMIN'
},
role: {
type: String,
default: 'USER',
enum: ['USER', 'MODERATOR', 'ADMIN'],
immutable: true
}
});
Sources: What's New in Mongoose 5.6.0: Immutable Properties and What's New in Mongoose 5.7: Conditional Immutability, Faster Document Arrays.
Upvotes: 35
Reputation: 861
You can do it with Mongoose only, in userSchema.pre
save
:
if (this.isModified('modified query')) {
return next(new Error('Trying to modify restricted data'));
}
return next();
Upvotes: 1
Reputation: 21
I had the same problem with field modifications.
Try https://www.npmjs.com/package/mongoose-immutable-plugin
The plugin will reject each modification-attempt on a field and it works for
It supports array, nesting objects, etc. types of field and guards deep immutability.
Plugin also handles update-options as $set, $inc, etc.
Upvotes: 1
Reputation: 105
Please be aware that the documentation explicitly states that when using functions with update in their identifier/name, the 'pre' middleware is not triggered:
Although values are casted to their appropriate types when using update, the following are not applied:
- defaults
- setters
- validators
- middlewareIf you need those features, use the traditional approach of first retrieving the document.
Model.findOne({ name: 'borne' }, function (err, doc) { if (err) .. doc.name = 'jason bourne'; doc.save(callback); })
Therefore either go with the above way by mongooseAPI, which can trigger middleware (like 'pre' in desoares answer) or triggers your own validators e.g.:
const theOneAndOnlyName = 'Master Splinter';
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
default: theOneAndOnlyName
validate: {
validator: value => {
if(value != theOneAndOnlyName) {
return Promise.reject('{{PATH}} do not specify this field, it will be set automatically');
// message can be checked at error.errors['username'].reason
}
return true;
},
message: '{{PATH}} do not specify this field, it will be set automatically'
}
}
});
or always call any update functions (e.g. 'findByIdAndUpdate' and friends) with an additional 'options' argument in the form of { runValidators: true }
e.g.:
const splinter = new User({ username: undefined });
User.findByIdAndUpdate(splinter._id, { username: 'Shredder' }, { runValidators: true })
.then(() => User.findById(splinter._id))
.then(user => {
assert(user.username === 'Shredder');
done();
})
.catch(error => console.log(error.errors['username'].reason));
You can also use the validator function in a non-standard way i.e.:
...
validator: function(value) {
if(value != theOneAndOnlyName) {
this.username = theOneAndOnlyName;
}
return true;
}
...
This does not throw a 'ValidationError' but quietly overrides the specified value. It still only does so, when using save()
or update functions with specified validation option argument.
Upvotes: 1
Reputation: 400
You can use Mongoose Immutable. It's a small package you can install with the command below, it allows you to use the "immutable" property.
npm install mongoose-immutable --save
then to use it:
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
immutable: true
}
});
userSchema.plugin(immutablePlugin);
Upvotes: 0