MongoDB document naming - javascript

I have tried so many time different things but unable understand that whenever I am saving data into MongoDB why am I getting the name of that document as below
User.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: {
type: String,
require: true,
unique: true,
lowercase: true,
},
username: {
type: String,
require: true,
unique: true,
maxLength: 7,
},
password: {
type: String,
require: true,
minLength: 8,
},
});
const User = mongoose.model("user", userSchema);
module.exports = User;
When I have saved the data by hitting API through postman and printing the data in console, the data I am receiving is as below:
email {
_id: new ObjectId("627a4ae94b8958e3fe968311"),
email: 'abc#abc.com',
username: 'fzee07',
password: 'test1234',
__v: 0
}
So my question is this that why this object is getting saved and named as "email".
registerUser.js
const User = require("../../models/User");
const registerUser = async (req, res) => {
const { email, username, password } = req.body;
try {
const e_mail = await User.findOne({});
console.log("email", e_mail);
if (e_mail.email === email) {
res.status(409).json({
success: false,
msg: "User already exists",
});
} else {
if (username.length < 8) {
if (password.length >= 8) {
const user = User.create({ email, username, password });
res.status(201).json({
success: true,
data: "User Created",
});
} else {
res.status(401).json({
success: false,
data: "Password muste be minimum 8 characters",
});
}
} else {
res.status(401).json({
success: false,
data: "Username must be less than 8 characters",
});
}
}
} catch (err) {
console.log(err);
}
};
module.exports = registerUser;

I understand the problem now. Sorry about the confusion, I misread the second part of your question. The reason you're getting this in console:
email {
_id: new ObjectId("627a4ae94b8958e3fe968311"),
email: 'abc#abc.com',
username: 'fzee07',
password: 'test1234',
__v: 0
}
Is because you are console logging the string "email", then your user object from the database, which you have confusingly named e_mail.
Change the log message to look like this, and you're good:
console.log("user", e_mail);
In fact, your code needs some severe refactoring. Right now, it's not very readable. Here's an improvement:
const User = require('../../models/User');
const registerUser = async (req, res) => {
const { email, username, password } = req.body;
try {
// Find a user with that email
const user = await User.findOne({ email });
console.log('user', user);
// if it exists, return
if (user) {
res.status(409).json({
success: false,
message: 'User already exists',
});
return;
}
// Validate the username and password
// If validates, create the user
if (username.length < 8 && password.length >= 8) {
const newUser = User.create({ email, username, password });
res.status(201).json({
success: true,
message: 'User created',
data: newUser,
});
return;
}
// If all else fails, return 401
res.status(401).json({
success: false,
message: 'Username must be less than 8 characters & password must be at least 8 characters long',
});
} catch (err) {
res.status(500).json({
success: false,
message: 'Server error',
});
}
};
module.exports = registerUser;
I changed all the places where you had the key of data to have the key of message, as messages were the values.
Once again though, I really recommend doing the validation in Mongoose middleware/hooks instead of right within the route. Will save you headaches as your application grows.

Related

Why I can't able to hash a password using Mongoose Virtual Setter?

I'm trying to 'setter' virtual method to hash my password. However, the password is not hashing. In fact, it is showing undefined every time I try to access the virtual field. If I try to store the data without the hash password then It stores the data. Some days ago, I tried the same code and it worked. But I don't know why It's not working now.
Here is My Code Link: https://github.com/RiyaadHossain/FlipkartClone-Server
Here is the controller code:
// Sign Up Controller_____________________________________
exports.signup = async (req, res) => {
// Check If the email already exists or not
User.findOne({ email: req.body.email }).exec((err, user) => {
if (user) {
res.status(400).json({ message: "Email Already Taken..!" });
}
if (err) {
res.status(500).json({ message: "An Internal Error Occured..!" });
}
});
const { firstName, lastName, email, userName, password } = req.body;
console.log(password); // Getting Undefined - Here is the problem
// Create New User
const newUser = new User({ firstName, lastName, email, userName, password }); // Error is with the password
newUser.save((err, data) => {
if (err) {
console.log(err);
res.status(500).json({ error: err.message });
}
if (data) {
res.status(500).json({ data });
}
});
};
Here is the virtual setter code:
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema(
{
firstName: {
type: String,
required: true,
trim: true,
min: 3,
max: 20,
},
lastName: {
type: String,
required: true,
trim: true,
min: 3,
max: 20,
},
userName: {
type: String,
required: true,
unique: [true, "User Name already Taken"],
index: true,
trim: true,
min: 3,
max: 20,
},
email: {
type: String,
unique: true,
},
hash_password: {
type: String,
required: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
contactInfo: String,
},
{
timestamps: true,
}
);
// Hash Password using Bcrypt
userSchema.virtual("password").set(function (password) {
this.hash_password = bcrypt.hashSync(password, 10);
});
// Instance Method - To compare password
userSchema.methods = {
authenticate: function (password) {
return bcrypt.compareSync(password, this.hash_password);
},
};
module.exports = new mongoose.model("User", userSchema);
Previous code Link Where I was able to do that:
Controller Code: https://github.com/RiyaadHossain/FlipkartClone-Practice-Server/blob/main/src/controller/auth.js
Model Code: https://github.com/RiyaadHossain/FlipkartClone-Practice-Server/blob/main/src/models/user.js
try change const newUser = new User({ firstName, lastName, email, userName, password }) to
const newUser = new User()
newUser.firstName = firstName
newUser.lastName = lastName
newUser.email = email
newUser.userName = userName
newUser.password = password

Struggling to patch a record in mongodb, unique not working and req.body undefined

I'm learning node.js and it's amazing, especially with mongo, but sometimes I struggle to solve a simple problem, like patching only 1 attribute in my user database.
It's easier to patch something that cannot be unique, but I want to patch an username attribute and I defined it as "unique" in my schema. I don't know why, but MongoDB doesn't care other db entry has the same user, it let me save.
My schema:
/** #format */
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema(
{
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true, unique: true },
userNumber: { type: Number, required: true },
description: { type: String },
verified: { type: Boolean, default: false },
isAdmin: { type: Boolean, default: false },
isSubscriber: { type: Boolean, default: false },
isDisabled: { type: Boolean, default: false },
acceptedTerms: { type: Number, required: true },
},
{ timestamps: true }
);
module.exports = mongoose.model('User', userSchema);
On my user controllers in node, I want to updateOne({ _id: userId}, { username: myNewUsername} but it always happens, it doesn't take into consideration another db entry can have the username, so I tried a different strategy but it doesn't work:
exports.changeUsername = (req, res, next) => {
// Requirements
const userId = req.params.userId;
const newUsername = req.body.username;
console.log('userId: ' + userId);
console.log('newUsername: ' + req.body.username);
User.findOne({ username: req.body.username })
.then(result => {
console.log(result);
if (result.username) {
const error = new Error('Could not find this sport');
error.code = 'DUPLICATED';
throw error;
}
return;
})
.catch(err => next(err));
// if no username was in use then updateOne
User.updateOne({ _id: userId }, { username: newUsername })
.then(result => {
res.status(200).json({
message: 'username has been updated',
username: result.username,
});
})
.catch(err => next(err));
};
I don't know if I can updateOne at the same time add some find validation. What I am doing wrong? Users cannot have the same username.
On the console, it seems it works, but it throws an extra error I don't understand:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (/Users/username/Sites/pipi-api/node_modules/express/lib/response.js:776:10)
I tried this other approach and it works, but doesn't trigger an error if the record is not unique as I stated in the schema.
// GET ONLY ONE SPORT BY ID
exports.changeUsername = async (req, res, next) => {
// Requirements
const userId = req.params.userId;
const newUsername = req.body.username;
console.log('userId: ' + userId);
console.log('newUsername: ' + req.body.username);
try {
const oldUsername = await User.findOne({ username: newUsername });
if (oldUsername.username) {
throw new Error('Error: its duplicated');
}
const user = await User.findOneAndUpdate(
{ _id: userId },
{ username: newUsername },
{ new: true }
);
console.log('User successfully updated.');
return res.status(200).json({ success: true, user });
} catch (err) {
console.log('ERROR: ', err);
return res.status(400).json({ success: false });
}
};
If I uncomment the code above, it triggers an error if I find a record on the database that matches but it doesn't allow me to continue to my next line of codes I the username is not found on the db.
I get a new error:
userId: 6231bdef334afbde85ed9f43
newUsername: tetete
ERROR: TypeError: Cannot read properties of null (reading 'username')
at exports.changeUsername (/Users/user/Sites/pipi-api/v1/controllers/users/index.js:43:21)
That error is not related to Mongo. It means that you are trying to send a response and the response is already sent.
The issue is because you called both User.findOne and User.updateOne and both of them has .then handler. So the first one of these that finishes will send the actual response. In the moment the second one finished, the response is already send and the error is thrown because you are trying to send response again.
Mongo will throw the error if you try to change username property that some other user already have. You should check if the req.params.userId and req.body.username sent correctly to the backend. Try to console.log() them and check if they are maybe null.
Consider refactoring your handler to use async/await instead of then/catch. You can do it like this:
exports.changeUsername = async (req, res, next) => {
try {
const userId = req.params.userId;
const newUsername = req.body.username;
const user = await User.findOneAndUpdate({ _id: userId }, { username: newUsername }, { new: true });
console.log('User successfully updated.');
return res.status(200).json({ success: true, user });
} catch (error) {
console.log('ERROR: ', error);
return res.status(400).json({ success: false });
}
}

How to fix: ReferenceError: validateEmail is not defined

I'm trying to make Register using EJS. Therefore, I'm checking
If all input fields have values or not
Is Email valid or not
Password Length has to be < 6 characters
Is Email already register or not
And give them a message if they do not comply with the above conditions. To Check all these conditions I have the following code inside the userCtrl.js file
userCtrl.js
const Users = require("../models/userModel");
const userCtrl = {
//! Register User
register: async (req, res) => {
try {
const { name, email, password } = req.body;
// Check If All fields are filled with values or not
if (!name || !email || !password) {
return res.status(400).json({ masg: "Please fill allfields." });
}
// Check If email is valid
if (!validateEmail(email)) {
return res.status(400).json({ masg: "Please enter valid email." });
}
//Check password length
if (password.length < 6) {
return res
.status(400)
.json({ masg: "Password must be atleast 6 characters long." });
}
const user = await Users.findOne({ email });
// Check If email is already registered
if (await Users.findOne({ email })) {
return res.status(400).json({ masg: "User already exists." });
}
res.json({ msg: "User Registered Successfully!" });
} catch (err) {
console.log(err);
return res.status(500).json({ msg: err.message });
}
},
};
//! Exporting User Controller
module.exports = userCtrl;
Here is user Module for refrance.
const mongoose = require("mongoose");
const { Schema } = mongoose;
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please enter your name"],
trim: true,
},
email: {
type: String,
required: [true, "Please enter your email"],
trim: true,
unique: true,
},
password: {
type: String,
required: [true, "Please enter your password"],
trim: true,
},
role: {
type: Number,
default: 0, // 0 for user, 1 for admin
},
avator: {
type: String,
default:
"https://res.cloudinary.com/manix/image/upload/v1639722719/avator/istockphoto-1214428300-170667a_c4fsdt.jpg",
},
});
//! Exporting User Modules
module.exports = mongoose.model("Users", userSchema);
But when I try to register users using Postman then I got this error.
enter image description here
Please Help me to fix this issue.
The issue is probably happening here
if (!validateEmail(email))
Where is your validateEmail() function located?

mongoose schema method returning undefined

I want to create a method that validates the user's password by using bcrypt.compare()
here is the code below.
UserSchema.methods.validatePassword = async (data) => {
console.log(this.email); // returns undefined
console.log(this.first_name); // returns undefined
return await bcrypt.compare(data, this.password);
};
here is the UserSchema I created
const UserSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } }
);
when getting this.password in my schema .pre('save', ..) it works but shows undefined when I use schema methods. :(
here is the implementation of the method
const verifySignIn = async (req, res, next) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({
status: 'failed',
message: 'User Not found.',
});
}
const isValid = await user.validatePassword(password);
if (!isValid) {
return res.status(401).send({
message: 'Invalid Password!',
data: {
user: null,
},
});
}
next();
} catch (err) {
Server.serverError(res, err);
}
};
In the guide it says:
Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document ...
So in this case, you just need to change UserSchema.methods.validatePassword = async (data) => {... to UserSchema.methods.validatePassword = async function(data) {...

Why do I get a 400 error when logging a user using bcrypt?

I'm trying to create login authentication, but I keep getting an 400 error in Postman saying that my syntax is bad when testing things out. I'm pretty sure my entire User model is solid, but for good measure, I've attached the whole thing in case something's off there. Otherwise, I'm really not sure what the problem is or where to go from here.
This is the data that I'm sending that triggers the 400 Bad Request (the request cannot be fulfilled due to bad syntax) and logs the invalid password to the console:
{
"email": "andrew#example.com",
"password": "Red12345!"
}
Here's my entire user model code:
const mongoose = require('mongoose')
const validator = require('validator')
const bcrypt = require('bcryptjs')
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
unique: true,
require: true,
trim: true,
lowercase: true,
validate(value) {
if(!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
age: {
type: Number,
default: 0,
validate(value) {
if(value < 0) {
throw new Error('Age must be a positive number.')
}
}
},
password: {
type: String,
trim: true,
lowercase: true,
required: true,
minlength: 7,
validate(value) {
if(value.toLowerCase().includes("password")) {
throw new Error("Password can't be 'password'.")
}
}
}
})
userSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email })
if (!user) {
throw new Error('User not found')
}
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
throw new Error('Invalid password')
}
return user
}
//Hash the plain text password before saving
userSchema.pre('save', async function (next) {
const user = this
if(user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8)
}
next()
})
const User = mongoose.model('User', userSchema)
module.exports = User
And here's the user login router:
router.post('/users/login', async (req, res) => {
try {
const user = await User.findByCredentials(req.body.email, req.body.password)
res.send(user)
} catch (e) {
console.log(e.message)
res.status(400).send()
}
})

Categories

Resources