'Mongoose findOne and save create new object and trigger uniquevalidator

I'm using mongo 4.4.10 (due to synology limitation) Express, mongoose and UniqueValidator 3.0.0 (the principal suspect)

When doing this

    router.get('/test',async (req,res)=>{
      try {
        const user = await UserModel.findById('abc'); //it find the user with id abc
        user.firstname = 'testOk';
        await user.save(); // <-- error and server crash         
        return res.status(200).json({message:'ok'});
      } catch (error) {
        return res.status(401).json({message:'not ok',error:error});
      }
      
    })

The res.status return 401 with message not ok

Error Message:

    > properties: {
    >         message: 'Error, expected `_id` to be unique. Value: `6222469034b2ff0a853f7b78`',
    >         type: 'unique',
    >         validator: [Function (anonymous)],
    >         path: '_id',
    >         value: ObjectId {
    >           [Symbol(id)]: Buffer(12) [Uint8Array] [
    >              98,  34, 70, 144, 52,
    >             178, 255, 10, 133, 63,
    >             123, 120
    >           ]
    >         }
    >       },
    >       kind: 'unique',
    >       path: '_id',
    >       value: ObjectId {
    >         [Symbol(id)]: Buffer(12) [Uint8Array] [
    >            98,  34, 70, 144, 52,
    >           178, 255, 10, 133, 63,
    >           123, 120
    >         ]
    >       },
    >       reason: undefined,
    >       [Symbol(mongoose:validatorError)]: true
    >     }   },   _message: 'user validation failed' } [nodemon] app crashed - waiting for file changes before starting...

My user schema is as follow :

    import mongoose from 'mongoose';
    import uniqueValidator from 'mongoose-unique-validator';
    import bcrypt from 'bcryptjs';
    
    const Schema = mongoose.Schema;
    
    const UserSchema = new Schema({
      email: {
        type: String,
        trim: true,
        lowercase: true,
        required: true,
        unique: true,
      },
      firstname:{
        type: String,
        trim: true,
        required: true
      },
      lastname:{
        type: String,
        trim: true,
        required: true
      },
      
      password: {
        type: String,
        required: true,
        select: false
      }
    }, { timestamps: true });
    
    
    UserSchema.pre('save', function(next) {
      const user = this;
      // only hash the password if it has been modified (or is new)
      if (!user.isModified("password")) return next();
      // generate a salt
      bcrypt.genSalt((err, salt) => {
        if (err) return next(err);
        // hash the password using our new salt
        bcrypt.hash(user.password, salt, function (err, hash) {
          if (err) return next(err);
          // override the cleartext password with the hashed one
          user.password = hash;
          next();
        });
      });
    });
    
    UserSchema.methods.isValidPassword = function(candidatePassword, cb) {
      const user = this;
      bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
          if (err) return cb(err);
          cb(null, isMatch);
      });
    };
    
    UserSchema.plugin(uniqueValidator);
    
    const UserModel = mongoose.model('user', UserSchema);
    
    
    export default UserModel;

Why mongoose is trying to create a new object ? Thanks for help

UPDATE

The issue seems to come from the module mongoose-unique-validator 3.0.0, when I comment the UserSchema.plugin(uniqueValidator); it works



Solution 1:[1]

It's because .save() is asynchronous, so you have to wait for it's response. Currently, your code will not wait for the response, and it will return 200 response message before the response from .save() is returned. You should add await:

await user.save();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 NeNaD