'How to catch the error when inserting a MongoDB document which violates an unique index?
I'm building a MEAN app.
This is my Username schema, the username should be unique.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('User', new Schema({
username: { type: String, unique: true }
}));
On my post route I save the user like this:
app.post('/authenticate', function(req, res) {
var user = new User({
username: req.body.username
});
user.save(function(err) {
if (err) throw err;
res.json({
success: true
});
});
})
If I post with the same username again I get this error:
MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index:
Can someone explain how instead of the error to send a json like { succes: false, message: 'User already exist!' }
Note: After I post the user I will automatically authentificate, dont need password or something else.
Solution 1:[1]
You will need to test the error returned from the save method to see if it was thrown for a duplicative username.
app.post('/authenticate', function(req, res) {
var user = new User({
username: req.body.username
});
user.save(function(err) {
if (err) {
if (err.name === 'MongoError' && err.code === 11000) {
// Duplicate username
return res.status(422).send({ succes: false, message: 'User already exist!' });
}
// Some other error
return res.status(422).send(err);
}
res.json({
success: true
});
});
})
Solution 2:[2]
You can also try out this nice package mongoose-unique-validator which makes error handling much easier, since you will get a Mongoose validation error when you attempt to violate a unique constraint, rather than an E11000 error from MongoDB:
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
// Define your schema as normal.
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true }
});
// You can pass through a custom error message as part of the optional options argument:
userSchema.plugin(uniqueValidator, { message: '{PATH} already exists!' });
Solution 3:[3]
2022 Update.
Looks like the err.name changed. Before, this error was returning as a MongoError, but now it is a MongoServerError. There's a whole story about Mongoose not handling MongoError directly, basically when a ServerError appears mongoose return it as it is.
NOTE: violating the constraint returns an E11000 error from MongoDB when saving, not a Mongoose validation error.## Heading ##
But now, this error is not a MongoError anymore, it's a MongoServerError now, which extends MongoError https://mongodb.github.io/node-mongodb-native/4.0/classes/mongoerror.html
Here two working examples:
app.post('/authenticate', function(req, res) {
var user = new User({
username: req.body.username
});
user.save(function(err) {
if (err) {
if (err.name === 'MongoServerError' && err.code === 11000) {
// Duplicate username
return res.status(422).send({ success: false, message: 'User already exist!' });
}
// Some other error
return res.status(422).send(err);
}
res.json({
success: true
});
});
})
async function store(req: Request, res: Response) {
const { email, password }: IUser = req.body;
const user: IUser = new User({
email: email,
password: await hashPassword(password),
});
user
.save()
.then(result => {
return res.status(201).json({
message: 'Successful registration.',
data: { email: result.email },
});
})
.catch(err => {
if (err.name === 'MongoServerError' && err.code === 11000) {
//There was a duplicate key error
return res.status(400).json({
message: 'Email already in use.',
data: { err },
});
}
return res.status(400).json({
message: "You didn't give us what we want!",
data: { err },
});
});
}
Solution 4:[4]
Try this:
app.post('/authenticate', function(req, res) {
var user = new User({
username: req.body.username
});
user.save(function(err) {
if (err) {
// you could avoid http status if you want. I put error 500
return res.status(500).send({
success: false,
message: 'User already exist!'
});
}
res.json({
success: true
});
});
})
Solution 5:[5]
Here's how you validate it using the type error instead of string:
// your own error in a diff file
class UniqueError extends Error {
constructor(message) {
super(message)
}
}
// in your service file
const { MongoError } = require('mongodb')
class UserService {
async createUser(userJSON) {
try {
return await User.create(userJSON)
} catch (e) {
if (e instanceof MongoError && e.code === 11000) {
throw new Error('Username already exist')
}
throw e
}
}
}
// in your controller file
class UserController {
async create(req, res) {
const userJSON = req.body
try {
return res.status(201).json(await userService.createUser(userJSON))
} catch (e) {
if (e instanceof UniqueError) {
return res.status(422).json({ message: e.message })
}
return res.status(500).json({ message: e.message })
}
}
}
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 | Martijn van Wezel |
| Solution 2 | chridam |
| Solution 3 | Waifu_Forever |
| Solution 4 | Jose Mato |
| Solution 5 |
