I'm trying out Sequelize's built in validators. Model definition:
module.exports = function(sequelize, DataTypes) {
var Device = sequelize.define('Device', {
model_name: {
type: DataTypes.STRING,
validate: {
notNull: {
args: true,
msg: 'Model name needed'
}
}
}
...
with bulkCreate
Device.bulkCreate(csvOutput, { validate: true })
.then(function() {
console.log('Records inserted into database')
})
.catch(function(err) {
console.log('Error encountered: ' + err.message)
})
To test, I tried to insert a blank field for model_name. I tried also to include a proper model name.
Both scenarios above produce an error that is simply [object Object]. I could not seem to figure out why. Hope someone can shed some light here!
Note: the error goes away if i remove validate from the Model entirely.
I found the answer, and i'm posting it here in case this could be helpful to anyone.
Changing this line console.log('Error encountered: ' + err.message) to just console.log(err) allows the error message to display properly on the console.
The string concatenation prevents the error message from showing (allowing only [object Object] to be displayed).
With the error message, the problem became obvious the validation notNull is deprecated and should be replaced by the column attribute allowNull (note this is a column attribute, not a validator)
Related
I need help with my code.
Trying to figure what is the best way to give some errors if specific fields already exist.
Add if statement maybe on the client side if the user trying to save with a name that already exists.
Or add a statement on the server side, something I'm trying to do right now...
Here is my app.post code;
app.post('/save-file', async(req, res) => {
// Test nr3;
const saveExists = await savefile.exists({
saveFileID: req.body.saveFileID
})
if (saveExists) console.log("Save file exists")
// Note: This will log if field exist. But I need to stop somehow...
// My original code;
const SaveDB = new savefile(req.body)
try {
await SaveDB.save()
res.status(201).json({
status: 'Success',
data: {
SaveDB
}
})
} catch (error) {
res.status(500).json({
status: 'Failed',
message: error
})
}
})
Example, if my "savefiles" on my mongoDB have field --> saveFileID = "game01"
and the user writes that name, then that code should stop and give some error,
else add a new schema.
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!
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";
}
}
I'm using Sequelize 2.0.0-rc3 and there's an error I'm encountering; it seems like I'm writing the migration correctly but I'm getting an error trying to run it. I'm using Postgresql on the backend. Everything seems to be working fine; this is a new, isolated issue. The Document table and id column exists (created it in a previous migration, but discovered it's not auto-incrementing id's; so tried creating this migration to add auto-incrementing).
var p = require('bluebird');
module.exports = {
up: function (migration, DataTypes, done) {
var promises = [];
promises.push(
migration.changeColumn(
'Document',
'id',
{
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
}
));
p.all(promises).then(done.bind(null, null)).catch(function (err) {
console.error('Migration Failed: ', err);
done(err);
});
},
down: function (migration, DataTypes, done) {
done();
}
};
Possibly unhandled TypeError: Cannot call method 'push' of undefined
at Object.module.exports.QueryGenerator.dataTypeMapping (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/lib/dialects/postgres/query-generator.js:848:32)
at Object.module.exports.QueryGenerator.pgDataTypeMapping (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/lib/dialects/postgres/query-generator.js:843:19)
at Object.module.exports.QueryGenerator.changeColumnQuery (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/lib/dialects/postgres/query-generator.js:250:31)
at module.exports.QueryInterface.changeColumn (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/lib/query-interface.js:345:37)
at module.exports.Migration.(anonymous function) [as changeColumn] (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/lib/migration.js:26:50)
at /Users/csimpson/code/temp-cause-server/database/migrations/20141216000001-alter-id-increment.js:8:41
at tryCatch1 (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/node_modules/bluebird/js/main/util.js:45:21)
at Promise$_callHandler [as _callHandler] (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/node_modules/bluebird/js/main/promise.js:660:13)
at Promise$_settlePromiseFromHandler [as _settlePromiseFromHandler] (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/node_modules/bluebird/js/main/promise.js:675:18)
at Promise$_settlePromiseAt (/Users/csimpson/code/temp-cause-server/node_modules/sequelize/node_modules/bluebird/js/main/promise.js:845:14)
I ran into the Possibly unhandled TypeError: Cannot call method 'push' of undefined error as well. I was also trying to add an id field to an existing table, one I had created in the previous migration.
My fix had two parts:
1) Add unique: true to the id column.
2) Undo the previous migration (where I created the table that I want to add an id column to), and then run both migrations at once.
Until I did both of these, my migration attempts failed.
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");