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.
Related
I am attempting to log a user in to my DB. When I log the user in, it returns the first userId in the DB and not the user who logged in. I have been struggling with this for a while and really am at a dead end.
This is my POST route to log the user in:
// login
router.post("/login", async (req, res) => {
const user = await User.findOne({
email: req.body.email,
});
const secret = process.env.SECRET;
if (!user) {
return res.status(400).send("the user not found!");
}
if (user && bcrypt.compareSync(req.body.password, user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
secret,
{ expiresIn: "1d" }
);
res.status(200).send({ user: user.email, token: token });
} else {
res.status(400).send("password is wrong!");
}
});
The const user = await User.findOne({ email: req.body.email, }); this returns the wrong user.
When I query the endpoint get a users profile with the userId it gets the right information. So its got nothing to do with the DB.
This is the call in the app.
const handleSubmit = () => {
axios
.post(`${baseURL}users/login`, {
email: email,
passwordHash: password,
})
.then(res => {
console.log('USER ID TOKEN', res.data.token);
setbearerToken(res.data.token);
AsyncStorage.setItem('bearerToken', res.data.token);
const decoded = decode(res.data.token);
setTokenID(decoded.userId);
dispatch(setUser(res.data));
});
};
user.js model
const userSchema = mongoose.Schema({
contactName: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
phone: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
passwordHash: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
token: {
type: String,
},
isAdmin: {
type: Boolean,
default: false
},
clubName: {
type: String,
required: true,
},
clubAddress: {
type: String,
required: true,
},
clubEmail: {
type: String,
required: true,
},
clubPhone: {
type: String,
required: true,
},
clubWebsite: {
type: String,
required: true,
},
clubContact: {
type: String,
required: true,
},
})
Your schema doesn't have a field email to filter on.
const user = await User.findOne({
email: req.body.email,
});
Maybe you try clubEmail field. I reproduced the behavior and it looks like that mongoose ignores the filter if the field does not exist in the Schema an just returns the first document in the collection.
E.g.
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({name: "Superman"}, ...
Returns the user with name "Superman".
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({xname: "Superman"}, ...
But when using xname in the filter document which does not exist in my schema neither in the collection as field the query returns the first document in my test collection (its not Superman).
Also look here similar issue: Model.find Mongoose 6.012 always return all documents even though having filter
Issue reported: https://github.com/Automattic/mongoose/issues/10763
Migration Guide to Mongoose 6:
https://mongoosejs.com/docs/migrating_to_6.html#strictquery-is-removed-and-replaced-by-strict
facing a small issue that whenever I try to update a 'comments' property it automatically overwrites old property, doesn't add next value to the array. Tried many options like adding $set parameter as option, removing it, adding overwrite: false, but not successfully. Been looking at the docs, but feels like I'm something missing and even docs can't help me.
My Model:
const mongoose = require("mongoose");
const TicketSchema = new mongoose.Schema({
title: {
type: String,
minlength: 5,
maxlength: 50,
required: [true, "Please validate title"]
},
description: {
type: String,
minlength: 10,
maxlength: 200,
required: [true, "Please validate description"]
},
comments: {
type: Array,
default: []
},
status: {
type: String,
enum: ["in progress", "resolved", "pending"],
default: "pending",
},
priority: {
type: String,
enum: ["regular", "medium", "high"],
default: "regular"
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: "User",
required: [true, "Please provide user"],
}
}, {
timestamps: true,
});
module.exports = mongoose.model('Ticket', TicketSchema);
My controller:
const updateUser = async (req, res) => {
const {
body: {username, email, firstname, lastname, country, password, comments},
params: {
id: employeeId
}
} = req;
const user = await User.findByIdAndUpdate(employeeId, req.body, {
$set: {
comments: req.body.comments
}
}, {
new: true, runValidators: true, upsert: true
}).select(["-password"]);
// Don't allow IT users to change credentials for IT and ADM users.
if (user.type.startsWith("IT") || user.type.startsWith("ADM")) throw new NotFoundError(`No user with id ${employeeId}`);
if (!user) throw new NotFoundError(`No user with id ${employeeId}`);
res.status(StatusCodes.OK).json({user});
};
You may want to use $push operator instead of $set.
Assuming req.body.comments is an array with comments, you could use $each to construct something like this:
const user = await User.findByIdAndUpdate(employeeId, req.body, {
$push: {
comments: { $each: req.body.comments }
}, {
new: true, runValidators: true, upsert: true
}
}).select(["-password"]);
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' });
})
});
})
This is my model profile.js
var mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String,
required: true
},
collegename:
{
type: String,
required: true
},
specialization:
{
type: String,
required: true
},
marks:
{
type: String,
required: true
},
courselevel:
{
type: String,
required: true
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
This is my route.js post function
router.post('/addprofiledetails', function(req, res, next) {
let newProfile = new Profile({
$educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
I got success msg in post function but the data not added in mongodb. i don't know where i did mistake .please help me.
In mongoDB I got data like,
{
"educationinfo": [],
"_id": "5bed14b93a107411a0334530",
"__v": 0
}
I want details inside educationinfo, please help.
You need to change schema definition and query.
1.remove required from schema Or apply required to those field that you must provide value.
educationinfo: [{
universityname:
{
type: String,
// required: true
},
degree:
{
type: String,
//required: true
},
coursecompletionyear:
{
type: String,
// required: true
},
collegename:
{
type: String,
// required: true
},
specialization:
{
type: String,
//required: true
},
marks:
{
type: String,
// required: true
},
courselevel:
{
type: String,
// required: true
}
}]
2.change $educationinfo with educationinfo
educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
Since you marked the properties of educationinfo as required, you need to provide them when you create an instance of Profile. If you don't want to do that you need to remove the required property from those properties that you won't be supplying on instance creation like below:
const mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String
},
collegename:
{
type: String
},
specialization:
{
type: String
},
marks:
{
type: String
},
courselevel:
{
type: String
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
After making those changes, you need to make one more change in your POST route, change $educationinfo to educationinfo
router.post('/addprofiledetails', function(req, res, next) {
const newProfile = new Profile({
educationinfo:[{
universityname: req.body.universityname,
degree: req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
The data you insert is incomplete.
The properties in your schema marked as required: true need to be inserted aswell. Because you do not meet the schema requirements, it is failing.
I 've a UserSchema that looks like:
export var UserSchema: Schema = new mongoose.Schema({
createdAt: Date,
email: {
type: String,
required: true,
trim: true,
unique: false,
},
firstName: {
type: String,
required: false,
trim: true
},
lastName: {
type: String,
required: false,
trim: true
},
password: {
type: String,
trim: true,
minlength: 6
},
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
And I 've a instance method like:
UserSchema.methods.printThis = () => {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
The method printThis is being called from
router.post('/signup', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
var user = new User(body);
console.log("created user as: ", user);
user.printThis();
});
Below is the output:
created user as: { email: 'prsabodh.r#gmail.com',
password: '123456',
_id: 59be50683606a91647b7a738,
tokens: [] }
========>>> PRINTING USER NOW
{}
You can see that the user is getting created properly. However, when I call printThis method on User - I'm not able to print the same user back and an empty {} is printed. How to fix this?
You shouldn't use arrow functions (=>) if the calling function is explicitly setting a context (which is what Mongoose does):
UserSchema.methods.printThis = function() {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
More info on arrow functions and their handling of this here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Arrow_functions
To get the _id value from the instance method can use _conditions that should work
UserSchema.methods.printThis = function(password) {
var user = this;
console.log(user._conditions['_id']);
};