Why User email validation failed? Mongoose ValidationError: - node.js

I wanted to extend my UserSchema to validate email
const userSchema = new mongoose.Schema(
{
username: {
type: String,
},
name: {
type: String,
},
email: {
type: String,
trim: true,
required: [true, 'Please add an email'],
unique: true,
lowercase: true,
},
I installed email-validator.
userSchema.path('email').validate((email) => {
if (!validator.validate('email')) { return false; }
if (!email) { return false; }
if (email.length === 0) { return false; }
return true;
}, 'Email must have a valid format!');
I made a POST request
"username" : "KoleSOmbor",
"name" : "Niki",
"email" : "koprivica#gmail.com",
Error
POST /api/v1/users 500 18.706 ms - 618
ValidationError: User validation failed: email: Email must have a valid format!
Why?

SOLVED
if (!validator.validate(email)) { return false; }
Works fine.

Related

I have been trying to solve the duplicate key problem in nodeJS with Mongoose, but nothing works

I'm trying to build a user model, but I want to make sure that username and email are unique. When I created the first user everything was ok, but when I try to create the second user with the same information, I got the some error that I can handle in when I will save, but the duplicate key wasn't there to handle it.
This is my schema file code:
const UserSchema = new Schema({
// this username with SchemaType of string
username: {
type: String,
lowercase: true,
required: [true, "username is required"],
unique: true,
trim: true,
minlength: [4, "try to user longer name"],
maxlength: [60, "your name is way too long"],
},
// virtual name
name: {
// name have two properties
// first is first and refer to first-name
// second is last and refer to last-name
first: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
},
last: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
}
},
password: {
type: String,
required: [true, "password is required"]
},
email: {
type: String,
required: [true, "email is required"],
unique: true
},
active: {
type: Boolean,
default: true
},
admin: {
type: Boolean,
default: false
},
meta: {
update: {
type: Date,
default: Date.now()
},
timestamp: {
type: Date,
default: Date.now()
}
}
});
UserSchema.virtual("fullname").get(function () {
// return the concatenation of first and last
return this.name.first + " " + this.name.last;
});
// Create User Model
const User = mongoose.model("User", UserSchema);
module.exports = User;
And this is my router code where I tried to handle it:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
console.log(err);
});
// save User in model
newUser.save()
// return response with info
return response.status(201).json(user);
})
I think the explanation here is quite a simple one. You are specifying the unique attribute in your schema for multiple fields, so mongo will not allow you to create multiple entries with the same information. This is quite obvious.
Also, I noticed a bit of irregularity in your code. The save method you are calling returns a promise, which means the event loop will not block your code and the response will be returned immediately. For this, you either need to handle your response inside the then block or use async await throughout your code.
I would suggest the following changes:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
if(err) {
response.status(403).json({ message: 'Your custom error message' });
}
newUser.save().then(res => {
return response.status(201).json(user);
}).catch(e => {
return response.status(500).json({ message: 'Your custom error message' });
})
});
})

minLength property in User model not working

Everything is working perfectly fine except for the minLength property of password.
If I send { "email" : "harshit#example.com", "password": "abc" } from Postman,
it still works even though I've set minLength to 6.
minLength property of email is working perfectly good but not of password.
server.js
app.post('/users', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
var user = new User(body);
user.save().then((doc) => {
res.send(doc)
}).catch( (err) => {
res.send(err)
})
});
user.js // using mongoose here.
var User = mongoose.model('User', {
email: {
type: String,
required: true,
trim: true,
minLength: 5,
unique: true,
validate: {
validator: validator.isEmail ,
message: `{VALUE} is not a valid E-Mail`
}
},
password: {
type: String,
required: true,
minLength: 6, // This line isn't working
trim: true
},
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
According to the Mongoose documentation you're supposed to use minlength, notice the lowercase l.

What is the correct way to make Validators with Mongoose

I had a userSchema, first with email, username and password fields with their own validators.
Then I wanted to add more fields like bio, location gender and birthday, also with their own validators.
const userSchema=new Schema({
email: { type: String, required: true, unique: true, lowercase: true, validate: emailValidators},
username: { type: String, required: true, unique: true, lowercase: true, validate: usernameValidators},
password: { type: String, required: true,validate: passwordValidators},
bio: { type:String,required:false,default:null,validate:bioValidators},
location: {type:String,required:false, default:null},
gender: {type:String,required:false,default:null,validate:genderValidators},
birthday: { type:Date,required:false,default:null}
});
Now the problem is that when i want to register, it's running every validators, so of course an error occured because in the register page i'm not even asking for the bio, location etc ..
I tried adding required:false but still not working and I didn't find any satisfying answer
EDIT
Here's how i create a new user
router.post('/register',(req,res)=>{
if (!req.body.email) {
res.json({success:false,message: 'You must provide an e-mail'});
}
else{
if (!req.body.username) {
res.json({success:false,message:'You must provide a username'});
}
else{
if(!req.body.password){
res.json({success:false,message:'You must provide a password'});
}
else{
let user= new User({
email: req.body.email.toLowerCase(),
username: req.body.username.toLowerCase(),
password: req.body.password
});
user.save((err)=>{
if (err) {
if (err.code === 11000) {
res.json({success:false,message:'Username or e-mail already exists'});
}else{
if (err.errors) {
if (err.errors.email) {
res.json({success:false, message:err.errors.email.message});
}
else{
if (err.errors.username) {
res.json({success:false,message:err.errors.username.message});
}
else{
if (err.errors.password) {
res.json({success:false,message:err.errors.password.message});
}
else{
res.json({ success:false,message:err});
}
}
}
}else{
res.json({success:false,message:'Could not save user :', err});
}
}
}
else{
res.json({success:true,message:'Account registered'})
}
});
}
}
}
});
The errors i get are bioValidator and genderValidator.
In fact my question is, how do i choose which validator is going to be executed.
EDIT
In order to make it work simply remove the required and validators field as follows:
const userSchema=new Schema({
email: { type: String, required: true, unique: true, lowercase: true, validate: emailValidators},
username: { type: String, required: true, unique: true, lowercase: true, validate: usernameValidators},
password: { type: String, required: true,validate: passwordValidators},
bio: { type:String, default:null},
location: { type:String, default:null},
gender: { type:String, default:null},
birthday: { type:Date, default:null}
});

Error while trying to save document in MongoDB

I have this signup method to save a user.
exports.signup = function(req, res) {
// Initialize the variables
var user = new User(req.body);
var message = null;
user.provider = 'local';
// save the user
user.save(function(err) {
if (err) {
console.log(err);
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
};
Her is my Schema.
var UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
index: true,
match: [/.+\#.+\..+/, "Please fill valid e-mail address"]
},
username: {
type: String,
trim: true,
unique: "Username should be unique",
required: true
},
password: {
type: String,
validate: [
function (password) {
return password && password.length > 6;
},
"Password should be greater than six letters"
]
},
salt: {
type: String
},
provider: {
type: String,
required: "Provider is required"
},
providerId: String,
providerData: {},
created: {
type: Date,
default: Date.now()
}
});
When I make a post request in an empty collection, the first is saved, but after that i am getting this error.
MongoError: Projection cannot have a mix of inclusion and exclusion.
at Function.MongoError.create (/home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/error.js:31:11)
at queryCallback (/home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/cursor.js:212:36)
at /home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/connection/pool.js:455:18
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
POST /signup 500 1445.231 ms - 615
Please help.
First req.body
{
"name":"John",
"email": "johndoe#gmail.com",
"username": "john123",
"password": "password"
}
Second req.body
{
"name":"Jane",
"email": "janedoe#gmail.com",
"username": "jane123",
"password": "password"
}

Mongoose force validation only if data is present

I am creating a user model that will create different Strategies like, local, facebook, gmail... and i want every object to do his own validation but if i does not put values in lets say local, i does not want to validate this fields and get an error.
For example:
var UserSchema = new Schema({
local: {
email : {
type : String,
required: true,
validate: emailValidator,
index : {
unique: true
}
},
firstName: {
type : String,
validate: nameValidator,
required: true
},
password : {
type : String,
validate: passwordValidator,
required: true
}
},
facebook: {
id : String,
email: String,
name : String
}
});
Now when i want to save some user that come from facebook like this:
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.name = profile.name.givenName;
newUser.facebook.email = profile.emails[0].value;
newUser.save(function( err, user ) {
if( err ) console.log(err);
done(null, user);
});
I will get an error because the local object validation failed. So how i can make them not depend on each other and still validate the data when insert the values?
You will have to create a custom validator and check if the proper strategy is set:
var emailValidator = [
{
validator: function(value) {
if(!this.local) return true;
return value;
},
msg: 'Email is required.'
},
{
validator: function(value) {
/* your current validation */
},
msg: 'Your error message...'
}
];
Just move your current emailValidator logic to the second function in this array and do this to the other required fields.

Resources