Leestex Leestex - 1 month ago 11x
Javascript Question

Mongoose custom validation of several fields on update

First of all, this doesn't help.

Let's say, we have a User model:

const schema = new mongoose.Schema({
active: { type: Boolean },
avatar: { type: String }

const User = mongoose.model('User', schema);

When we update it (set an avatar):

// This should pass validation
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

We want to validate it based on current (or changed)
attribute value.

Case #1

  • active

  • we should not be able to set avatar - it should not pass the validation

Case #2

  • active

  • we should be able to set avatar - it should pass the validation


  1. Use a custom validator

const schema = new mongoose.Schema({
active: { type: Boolean },
avatar: { type: String, validate: [validateAvatar, 'User is not active'] }

function validateAvatar (value) {
console.log(value); // user.avatar
console.log(this.active); // undefined

So this will not work as we don't have an access to

  1. Use pre "validate" hook

schema.pre('validate', function (next) {
// this will never be called

This hook doesn't work with

  1. Use pre "update" hook

schema.pre('update', function (next) {
console.log(this.active); // undefined

This will not work for us as it doesn't have an access to model fields.

  1. Use post "update" hook

schema.post('update', function (next) {
console.log(this.active); // false

This one works, but in terms of validation is not quite good choice, as the function is being called only when model was already saved.


So is there a way to validate the model based on several fields (both saved in DB and new ones) before saving it, while using

As a summary:

  1. Initial user object

{ active: false, avatar: null }

  1. Update

User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

  1. Validation should have an access to

{ active: false, avatar: 'user1.png' }

  1. If validation fails, changes should not be passed to DB


Due to limitation of working with update() I've decided to solve the problem this way:

  • Use custom validators (idea #1 mentioned in the question)
  • Don't use update()

So instead of

User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

I use

User.findOne({ _id: id })
    .then((user) => {
        user.avatar = 'user1.png';

In this case custom validators work as expected.

P.S. I choose this answer as a correct one for me, but I will give bounty to the most relevant answer.