How to catch errorcode in mongoose - node.js

Just playing around with my first login system, based on nodejs/express/mongoose/passport.
I have found more posts about this subject, but they all are slightly different then mine.
model:
module.exports = mongoose.model('User',{
id: String,
username: String,
password: String,
email: { type: String, unique: true }
});
For username a query is used to check if username is already taken. But that is not the way to do it. I have learned the database itself must check this.
So that is why I am trying it out on the email field.
saving a new user:
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
//throw err; // server stops working immediately!!
return done(null, false, req.flash('message','DB error...'));
}
console.log('User Registration succesful');
return done(null, newUser);
});
I added slashes before "throw err", because the node server stops immediately when trying to "throw".
I copied/pasted the return done(...) part, which works well.
console error message:
Error in Saving user: MongoError: insertDocument :: caused by :: 11000
E11000 duplicate key error index: mydb.users.$email_1 dup key: { :
"sub#xxxxx.nl" }
Problem:
If any database error occures, I cannot assume it always will be a duplicate insert error, so I need to check on this, so I can show a message about duplicate emailadres or another usefull message.
I can't find anything like:
if(errorcode == E11000)
message="duplicate email"
else
message="an error occured bla bla"

The error code is stored in err.code, so you can check for it like this:
if (err) {
if (err.code && err.code === 11000) {
message = "duplicate email";
} else {
message = "an error occured bla bla";
}
}

Related

mongoose-unique-validator detects all inputs as already existed

I am trying to create a unique email value in MongoDB. I used mongoose-unique-validator to implement that but resulted in error claiming that my unique emails that I just inputted are already existed.
This is the error message I received from trying to input unique email.
"message": "User validation failed: email: Error, expected email to be
unique., _id: Error, expected _id to be unique."
They said the Email and _id are not unique. But.., _id is an auto-generated value to be unique in MongoDB meaning that it should not be detected as a duplicated value.
I am not sure if that was caused by my own implementation so I would look forward to see any assumption or ways to debug to the root cause of this too. I tried restarting from fresh Mongo DB and manually inputting uniquely via Postman but still no hope.
These are a part of the codes that might be related to the data creation on MongoDB
UserModel.js
var uniqueValidator = require('mongoose-unique-validator');
const { Schema } = mongoose;
const UsersSchema = new Schema({
email: { type: String, unique: true, required: true},
hashedPassword: String,
salt: String,
});
UsersSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });
UsersSchema.set('autoIndex', false);
Server.js ---- /api/users/register
const finalUser = new Users(user);
finalUser.setPassword(user.password);
finalUser.save((err, data) => {
if (err) {
return res.status(400).json(err)
}
return res.json({ user: finalUser.toAuthJSON() })
})
Additional Information
I tried this solution from Sritam to detect another email with the same value but it still claims that the inputted email is already existed.
UsersSchema.path('email').validate(async (value) => {
const emailCount = await mongoose.models.User.countDocuments({email: value });
return !emailCount;
}, 'Email already exists');
"message": "User validation failed: email: Email already exists"
You can use validateModifiedOnly option in Document.save(). _id and email fields will never be validated unless they are modified. The code should look like this:
finalUser.save({ validateModifiedOnly: true }, (err, data) => {
if (err) {
return res.status(400).json(err)
}
return res.json({ user: finalUser.toAuthJSON() })
})
Found the issue and solution!
They are acting weird like in the question because the model was not initialized.
I must perform Schema.init() before performing any model validation.
The solution is to add UsersSchema.init().then(() => {...your operation})
Now Server.js ---- /api/users/register should look like this.
Users.init().then(() => { // where Users is my UsersModel
finalUser.save((err, data) => {
if (err) {
return res.status(400).json(err)
}
return res.json({ user: finalUser.toAuthJSON() })
})
})
Hope this helps other developers who experience similarly odd error!

Using connect-flash with sequelize

I am having trouble getting connect flash to work in the sequelize callback function.
router.route('/')
.post(function(aRequest, aResponse) {
var data = aRequest.body;
models.users.findOne({
where: {
email: data.email
}
}).then(function (aUser) {
if (!aUser) {
bcrypt.hash(data.password, null, null, function(err, hash) {
if(err) {
return next(err);
}
models.users.create({
firstname : data.firstname,
lastname : data.lastname,
email : data.email,
password : hash
}).then(function () {
aRequest.flash('success', 'User successfully created.');
}).done(function () {
aResponse.redirect('/login');
});
});
}
else {
// aRequest.flash('error', 'This email address is already registered');
aResponse.redirect('/login');
}
});
});
Above is my current code, have tried a few variations on it, calling both flash and redirect in the .then(), tried 2 .then()'s and now the .done().
I am getting the following error:
Unhandled rejection TypeError: undefined is not a function
at Instance.set (/home/node/shared/Heroku/landbou/node_modules/sequelize/lib/instance.js:348:68)
Which is easily resolved by removing aRequest.flash(...).
And yes, 'router.use(flash());' is being called higher up.
Everything continues so its not an app breaking error, but I do need the messages to flash up, otherwise I have to create additional wasted routes to handle the success/fail for user registrations.

Mongoose unique validation error type

I'm using this schema with mongoose 3.0.3 from npm:
var schema = new Schema({
_id: Schema.ObjectId,
email: {type: String, required: true, unique: true}
});
If I try to save a email that is already in db, I expect to get a ValidationError like if a required field is omitted. However this is not the case, I get a MongoError: E11000 duplicate key error index.
Which is not a validation error (happens even if I remove the unique:true).
Any idea why?
I prefer putting it in path validation mechanisms, like
UserSchema.path('email').validate(function(value, done) {
this.model('User').count({ email: value }, function(err, count) {
if (err) {
return done(err);
}
// If `count` is greater than zero, "invalidate"
done(!count);
});
}, 'Email already exists');
Then it'll just get wrapped into ValidationError and will return as first argument when you call validate or save .
I had some issues with the approved answer. Namely:
this.model('User') didn't work for me.
the callback done wasn't working properly.
I resolved those issues by:
UserSchema.path('email').validate(async (value) => {
const emailCount = await mongoose.models.User.countDocuments({email: value });
return !emailCount;
}, 'Email already exists');
I use async/await which is a personal preference because it is much neater: https://javascript.info/async-await.
Let me know if I got something wrong.
This is expected behavior
The unique: true is equivalent to setting an index in mongodb like this:
db.myCollection.ensureIndex( { "email": 1 }, { unique: true } )
To do this type of validation using Mongoose (Mongoose calls this complex validation- ie- you are not just asserting the value is a number for example), you will need to wire in to the pre-save event:
mySchema.pre("save",function(next, done) {
var self = this;
mongoose.models["User"].findOne({email : self.email},function(err, results) {
if(err) {
done(err);
} else if(results) { //there was a result found, so the email address exists
self.invalidate("email","email must be unique");
done(new Error("email must be unique"));
} else {
done();
}
});
next();
});
Simply response to json
try {
let end_study_year = new EndStudyYear(req.body);
await end_study_year.save();
res.json({
status: true,
message: 'បានរក្សាទុក!'
})
}catch (e) {
res.json({
status: false,
message: e.message.toString().includes('duplicate') ? 'ទិន្នន័យមានរួចហើយ' : e.message.split(':')[0] // check if duplicate message exist
})
}
Sorry for answering an old question. After testing I feel good to have find these answers, so I will give my experience. Both top answers are great and right, just remember that:
if your document is new, you can just validate if count is higher than 0, thats the common situation;
if your document is NOT new and has modified the unique field, you need to validate with 0 too;
if your document is NOT new and has NOT being modified, just go ahead;
Here is what I made in my code:
UserSchema.path('email').validate(async function validateDuplicatedEmail(value) {
if (!this.isNew && !this.isModified('email')) return true;
try {
const User = mongoose.model("User");
const count = await User.countDocuments({ email: value });
if (count > 0) return false;
return true;
}
catch (error) {
return false;
}
}, "Email already exists");

I am getting a duplicate key error index: when I am trying to save data in mongodb

I have a schema :
var RegisterInfoSchema= new Schema({
Organization:String,
NGOName:String,
Acronym:String,
Address:String,
Province:String,
District:String,
Tehsil:String,
Telephone_number:String,
Website:String,
Demographics:String,
Username:{type:String ,index: {unique:true}},
Password:String
})
exports.savePersonalInfo = function (req,res){
console.log("savePersInfo CALLED");
var receivedObj = new RegisterInfo({
Organization: req.body.regOrgType ,
NGOName: req.body.regName,
Acronym: req.body.regAcronym ,
Address: req.body.regAddress ,
Province: req.body.regProvince,
District: req.body.regDistrict,
Tehsil: req.body.regTehsil ,
Telephone_number: req.body.regTelNo ,
Website: req.body.regWebAddr,
Demographics: req.body.regDemographics,
Username: req.body.regUserName ,
Password: req.body.regPsw
});
receivedObj.save(function(err){
console.log("inside Save ");
if(err){
console.log(err);
}
else{
console.log("Saved!!");
res.send("");
}
});
}
There is indexing in Username
When I am trying to save data using save() method then it gives the following error:
{ [MongoError: E11000 duplicate key error index: testdb.registerinfos.$username_1 dup key: { : null }]
name: 'MongoError',
err: 'E11000 duplicate key error index: testdb.registerinfos.$username_1 dup key: { : null }',
code: 11000,
n: 0,
lastOp: 0,
connectionId: 339527,
ok: 1 }
You have a unique constraint on registerinfos.username and you're trying to save a document that has a value for that field that already exists in the database. I know this because that's what it says in the exception ;) It has nothing to do with _id values.
At Sammaye noted, your code is trying to create a new RegisterInfo doc each time savePersonalInfo is called. If you're also using this function to update existing docs you'll need to modify the function to do so.
In pseudo-code:
RegisterInfo.findOne({Username: req.body.regUserName}, function (err, reginfo) {
if (!err) {
if (!reginfo) {
// Doesn't exist, create new doc as you're doing now
} else {
// Does exist, update reginfo with the values from req.body and then
// call reginfo.save
}
}
});

Node.js + mongoose [RangeError: Maximum call stack size exceeded]

I am new to Node.js and I'm facing an error :
RangeError: Maximum call stack size exceeded
I'm not able able to solve the problem because most of the stack problems in others stackoverflow questions about Node.js deals with hundreds of callback but I have only 3 here.
First a fetch (findById) then an update an later a save operation!
My code is :
app.post('/poker/tables/:id/join', function(req, res) {
var id = req.params.id;
models.Table.findById(id, function(err, table) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
if (table.players.length >= table.maxPlayers) {
res.send({
message: "error: Can't join ! the Table is full"
});
return;
}
console.log('Table isnt Full');
var BuyIn = table.minBuyIn;
if (req.user.money < table.maxPlayers) {
res.send({
message: "error: Can't join ! Tou have not enough money"
});
return;
}
console.log('User has enought money');
models.User.update({
_id: req.user._id
}, {
$inc: {
money: -BuyIn
}
}, function(err, numAffected) {
if (err) {
console.log(err);
res.send({
message: 'error: Cant update your account'
});
return;
}
console.log('User money updated');
table.players.push({
userId: req.user._id,
username: req.user.username,
chips: BuyIn,
cards: {}
});
table.save(function(err) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
console.log('Table Successfully saved with new player!');
res.send({
message: 'success',
table: table
});
});
});
});
});
The error occurs during the save operation at the end!
I use MongoDb with mongoose so Table and User are my database collections.
This is from my first project with Node.js,Express.js and MongoDB so I probably have made huge mistakes in the async code :(
EDIT: I tried to replace the save with an update:
models.Table.update({
_id: table._id
}, {
'$push': {
players: {
userId: req.user._id,
username: req.user.username,
chips: BuyIn,
cards: {}
}
}
}, function(err, numAffected) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
console.log('Table Successfully saved with new player!');
res.send({
message: 'success',
table: table
});
});
But it doesn't help the error is still coming and I don't know how to debug it :/
I've been passing for this problem too.
Basically, when you have a property with a ref, and you want to use it in a find, for example, you can't pass the whole document.
For example:
Model.find().where( "property", OtherModelInstance );
this will trigger that error.
However, you have 2 ways to fix this for now:
Model.find().where( "property", OtherModelInstance._id );
// or
Model.find().where( "property", OtherModelInstance.toObject() );
This may stop your problems for now.
There is a issue in their GitHub repo where I reported this, however it's without fix for now. See the issue here.
I kept getting this error and finally figured it out. It's very hard to debug since no real information is presented in the error.
Turns out I was trying to save an object into a field. Saving only a specific property of the object, or JSON stringifying it, worked like a charm.
Seems like it would be nice if the driver gave a more specific error, but oh well.
MyModel.collection.insert causes:
[RangeError: Maximum call stack size exceeded]
When you pass array of instances of MyModel instead of just array with values of that objects.
RangeError:
let myArray = [];
myArray.push( new MyModel({ prop1: true, prop2: false }) );
MyModel.collection.insert(myArray, callback);
No error:
let myArray = [];
myArray.push( { prop1: true, prop2: false } );
MyModel.collection.insert(myArray, callback);
There are a few ways to debug nodejs applications
Built-in Debugger
The Node.js debugger is documented here: http://nodejs.org/api/debugger.html
To place breakpoints, simply put debugger; at the place you want to break. As you said the table.save callback is giving you troubles, you could put a breakpoint inside that function.
Then you run node with the debugger enabled:
node debug myscript.js
And you will get more helpful output.
Investigating the stack
You can also use console.trace to print a stacktrace, if you have a good idea of when/where you run into problems, and want to figure out how you got there.
Good luck, I hope this helps!

Resources