Reputation: 2205
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.
Upvotes: 24
Views: 40030
Reputation: 66
If you are using mongoose>4.5.0 you can use this error handling middleware based on their documentation:https://mongoosejs.com/docs/middleware.html#error-handling-middleware
//Checking for unique keys when you have multiple indexes
UserSchema.post("save", function (error, doc, next) {
if (error.name === "MongoServerError" && error.code === 11000) {
const keyPattern = Object.keys(error.keyPattern);
const key = keyPattern[0];
next(new Error(`${key} already taken!`));
} else {
next();
}
});
Upvotes: 3
Reputation: 1382
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 UniqueError('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 })
}
}
}
Upvotes: 0
Reputation: 107
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 },
});
});
}
Upvotes: 7
Reputation: 10899
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
});
});
})
Upvotes: 64
Reputation: 103425
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!' });
Upvotes: 8
Reputation: 2799
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
});
});
})
Upvotes: -1