Mongoose: How to prevent mongodb to save duplicate email records in database

Viewed 19825

I want to make the key email unique across that collection but i cant getting this working, here is my server code.

// Create a schema
var userSchema = new mongoose.Schema({
    email: { type: String, required: true},
    password: String
});

var userModel = mongoose.model("user", userSchema);

router.post('/postuser', (req, res) => {
    console.log('Requested data to server: ' + JSON.stringify(req.body._user));
var user = new userModel({
    email: req.body._user.email,
    password: req.body._user.password
});
// user.isNew = false;
user.save((err, data) => {
    console.log('Analyzing Data...');
    if(data) {
        console.log('Your data has been successfully saved.');
        res.json(data);
}
else {
  console.log('Something went wrong while saving data.');
  console.log(err);
  res.send(err);
}

})
});

Note: I also try email: { type: String, required: true, unique: true} but its not working and show below error.

name: 'MongoError',

message: 'E11000 duplicate key error collection: hutreservationsystem.users

index: _Email_1 dup key: { : null }',

driver: true,

code: 11000,

index: 0,

errmsg: 'E11000 duplicate key error collection: hutreservationsystem.users index: _Email_1 dup key: { : null }',

getOperation: [Function],

toJSON: [Function],

toString: [Function] }

6 Answers

A short answer using this tool mongoose-unique-validator

npm install --save mongoose-unique-validator

and in your model

var mongoose = require('mongoose')
var uniqueValidator = require('mongoose-unique-validator')
var userSchema = new mongoose.Schema({
    email: { type: String, required: true, unique: true},
    password: String
});

userSchema.plugin(uniqueValidator)
var userModel = mongoose.model("user", userSchema);

That's it! (Notice unique: true)

Now, there is no email duplication in your collection.

Bonus! : you can access err

.catch(err => console.log(err))

so in your example

// user.isNew = false;
user.save((err, data) => {
    console.log('Analyzing Data...');
    if(data) {
        console.log('Your data has been successfully saved.');
        res.json(data);
}
else {
  console.log('Something went wrong while saving data.');
  console.log(err);
  res.send(err);
}

accessing err >> so you can res.send(err.message) >> 'Validation failed'

{
    message: 'Validation failed',
    name: 'ValidationError',
    errors: {
        email: {
            message: 'Error, expected `email` to be unique. Value: `example@gmail.com`',
            name: 'ValidatorError',
            kind: 'unique',
            path: 'email',
            value: 'example@gmail.com'
        }
    }
    }
email: {
    type: String,
    trim: true,
    unique: true, // note - this is a unqiue index - not a validation
    validate: {
        validator: function(value) {
            const self = this;
            const errorMsg = 'Email already in use!';
            return new Promise((resolve, reject) => {
                self.constructor.findOne({ email: value })
                    .then(model => model._id ? reject(new Error(errorMsg)) : resolve(true)) // if _id found then email already in use 
                    .catch(err => resolve(true)) // make sure to check for db errors here
            });
        },
    }
},

ES6:

const userSchema = new Schema({
    name: String,
    id: {
        type: String,
        required: true,
        unique: true,
        validate: async (value) => {
            try {
                const result = await userModel.findOne({ id: value })
                if (result) throw new Error("duplicity detected: id :" + value);
            } catch (error) {
                throw new Error(error);
            }
        }
    }
})

const userModel = mongoose.model<Document>('users', userSchema);

ES6 (TypeScript):

const userSchema = new Schema({
    name: String,
    id: {
        type: String,
        required: true,
        unique: true,
        validate: async (value: any): Promise<any> => {
            try {
                const result: Document | null = await userModel.findOne({ id: value })
                if (result) throw new Error("duplicity detected: id :" + value);
            } catch (error) {
                throw new Error(error);
            }
        }
    }
})

const userModel: Model<Document, {}> = mongoose.model<Document>('users', userSchema);

In your user schema set attribute email as unique (unique: true).

var userSchema = new mongoose.Schema({ email: { type: String, required: true,  unique: true}, });
Related