In the user model schema, I give first_name to require and email to unique and require, when I save the data in the database I would like to send a response if the user will not send the first_name. how can we do this without add manually conditions?
I'm adding manually conditions for this operation
exports.saveuser = async (req, res) => {
const { first_name, email } = req.body;
if (!first_name || !email)
return res.status(401).json({ error: 'All the data require' });
const user = User.findOne({ email });
if (user) {
return res.status(401).json({ error: 'Email already exist' });
}
const user = new User({
first_name,
email,
});
await user.save();
};
in the above code, I add conditions manually, but I don't want to add all the conditions every time.
You can follow express Validator. It pretty industry standard. https://express-validator.github.io/docs/
Below is the example:
router.PUT(
`/api/user`,
auth,
validate(UserValidator.saveUser),
UserController.saveUser,
);
Related
I have a problem with my code now and I can't make it work. I am trying to check if the email added by the user is already in the database so I can generate an error message to the person and I don't know exactly how to check this. Can someone please help me? Thank you so much
Backend code now:
//Create user
app.post("/register/users", (req, res, next) => {
let userData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
};
const user = new User(userData);
user
.save()
.then((result) => {
res.status(200).json(result._id);
})
.catch((error) => res.status(422).json(error));
// console.log(res.body);
});
How can I check if user with similar email is already register to display the error? Thank you so much for your time
You can do a check before saving the user.
First, check if the user with the same email exists in the database.
If exist, do not save again, instead send the response.
If not exist, save the user.
app.post("/register/users", async (req, res, next) => {
let userData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
};
// checkin if a user with the mail exist
try {
const existingUser = await User.findOne({ email: req.body.email });
if (existingUser) {
// you may want to send different status code
return res.status(200).json({ message: 'User alreay registered });
}
const user = new User(userData);
// save the user
const newUser = await user.save();
return res.status(200).json(newUser._id);
} catch(error) {
return res.status(422).json({ error });
});
So let's say I want to make a Mongoose query to a database, inside of an Express post route:
app.post("/login",(req,res)=>{
const username = req.body.username
const password = req.body.password
User.find({username:username},(err,user)=>{
if (err) handleError(err)
//if user exists
if (user.length) {
//check password
if (user.password === password) {
//assign jwt, redirect
} else {
//"username/password is incorrect"
}
} else {
//"username/password is incorrect"
}
})
})
My concern is the handleError function. I'm not quite sure what kind of errors could even happen in Mongoose since it's just a simple query, but what should be included in the handleError function? And what response should I send to the user at that point?
You should in my opinion:
Use promises with async/await.
Don't catch any error(s) in your middleware and handle errors in the top-level express error handler. More on this here.
In your top-level express error handler, depending on the environment either return a simple message like: return res.status(500).json({ message: "Our server are unreachable for now, try again later." }); if this is in production. If you're in a local environment, return a JSON payload with the error in it like: return res.status(500).json({ err: <Error> });.
To sumerize, your code should look something like this:
app.post('/login', async (req, res) {
// ES6 Destructuring
const { username, password } = req.body;
// Use findOne instead of find, it speeds up the query
const user = await User.findOne({ username });
if (!user || (user.password !== hashFunction(password))) {
return res.status(403).json({ message: 'Bad credentials' });
}
// assign JWT and redirect
});
You can just send an error response with descriptive message related to Mongoose response.
app.post("/login",(req,res)=>{
const username = req.body.username
const password = req.body.password
User.find({username:username},(error,user)=>{
if (error){
return res.status(400).json({message:"Can not perform find operation.", error: error });
}
//if user exists
if (user.length) {
//check password
if (user.password === password) {
//assign jwt, redirect
} else {
//"username/password is incorrect"
}
} else {
//"username/password is incorrect"
}
})
})
What I am attempting to do is write a statement to check if email exists in my mysql database when a user registers. In postman it sends me the correct error message of "user already taken" however the server crashes after and displays "cannot set headers after they are sent to the client." I have read similar posts but did not help.
//The following code is in my user.service.js file:
const pool = require("../../config/database");
module.exports = {
//Create new user
createUser: (data, callBack) =>{
pool.query(
`insert into registration(name, email, password, confirm_password)
values(?,?,?,?)`,
[
data.name,
data.email,
data.password,
data.confirm_password
],
(error, results, fields) =>{
if(error){
return callBack(error);
}
return callBack(null, results);
}
);
}
}
//The following code is in my user.controller.js file:
const {
createUser,
} = require("./user.service");
const pool = require("../../config/database");
module.exports = {
createUser: (req, res) =>{
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
})
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
}
}
//The following code is from user.router.js file:
const {
createUser,
} = require("./user.controller");
const router = require("express").Router();
router.post("/signup", createUser);
module.exports = router;
In your createUser function that is executed on the post request you are doing two things. First you check whether a user with the provided email exists and, second, you create a user. However, those functions are not executed consecutively, instead they are running simultaneously and thus create a race condition.
So going off on your example, if the email check query SELECT email FROM registration WHERE email = ? is faster and the user already exists, it will respond with:
return res.status(400).json({
message: 'User already taken'
})
but the createUser function (below) is still running and once it is finished, it will try to also send a response. Therefore, you are presented with an application crash in the console even though in the postman you can see the response stating that the user already exists.
In order to fix this error you should execute the createUser function only if the results.length is 0 inside the callback provided to the email check query, like so:
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
})
}
Now you execute the createUser function only if a user with the provided email doesn't exist, which effectively removes the race condition between the two functions.
I am very new to coding and am writing a personal project using node.js, express, mongoDB, and mongoose. I wrote most of it myself, however I hired someone to help me with the more advanced parts. I have lost contact with him and went back under the hood to create an admin panel I could use to write blog posts and other things. I am trying to write a middleware that only allows myself access to the route. However it is not working.
function adminAuth(req, res, next){
if(req.user.isAdmin){
return next();
} else {
res.redirect("/");
}
}
I am a bit confused of the syntax he has used to create a user schema and I am not sure how to add this isAdmin key value pair. Any help updating my users with an isAdmin key value would be extremely appreciated, and also helping me finish the middleware as (req.user.isAdmin) is not working! (If I do not provide the necessary code, please excuse my inexperience and tell me what you would like to see).
Here is the Auth route the coder I hired wrote that I am having trouble deciphering how to pass in new data to the user model.
const isAdmin = false;
const passwordHash = await bcrypt.hash(req.body.password, saltRounds);
const db = client.db(dbName);
const col = db.collection('users');
const user = {
email, firstName, lastName, password: passwordHash, isAdmin,
};
local strategy
module.exports = function localStrategy() {
passport.use(new Strategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, (req, email, password, done) => {
const url = process.env.MONGOLAB_URI;
const dbName = 'giftgrab';
(async function addUser() {
let client;
try {
client = await MongoClient.connect(url);
const db = client.db(dbName);
const col = db.collection('users');
const user = await col.findOne({ email });
debug('Found user by email');
debug(user);
if (!user) {
req.flash('error', 'The username or password is wrong');
done(null, false);
} else {
const match = await bcrypt.compare(password, user.password);
if (match) {
done(null, user);
} else {
req.flash('error', 'The username or password is wrong');
// we pass null because it did not error, just failed
done(null, false);
}
}
} catch (e) {
debug(e.stack);
}
client.close();
}());
}
Here is the Auth route the coder I hired wrote that I am having trouble deciphering how to pass in new data to the user model.
// add logic to check if the user is admin
const isAdmin = false;
// user data collected here. If you want to add an "isAdmin" property, this is the right place
const user = {
email, firstName, lastName, password: passwordHash, isAdmin,
};
// checking if the user already exists
const check = await col.findOne({ email });
if (check) {
req.flash('error', 'The user with this email already exists');
res.redirect('back');
} else {
// the user does not exist, insert a new one and authenticate
const results = await col.insertOne(user);
req.login(results.ops[0], () => {
res.redirect('/');
});
}
This is what related to adding the isAdmin property. In order to use req.user and req.isAuthenticated() you are going to need Passport.js. The user data stored in you session (req.user) is defined by your passport strategy so if you want to use the isAdmin property this way, you are going to need to set it there.
I am getting a bcrypt error stating that data and hash arguments are required, referencing line #44 in my routes.js file. From what I can tell, I am passing that information: the first parameter to bcrypt.compare is the user entered password, and the second is the hashed password retrieved from the db. What am I doing wrong?
bcrypt.compare(req.params.password, user.password, function...
routes.js
'use strict'
var express = require('express');
var router = express.Router();
var User = require('../app/models/user');
//password hashing
var bcrypt = require('bcrypt');
var count = 0;
router.use(function(req, res, next) {
count++;
console.log('API hit count = %s', count);
next();
});
// /users post(create new user) get(specific user)
router.route('/users')
.post(function(req,res) {
var user = new User();
user.username = req.body.username;
user.password = bcrypt.hashSync(req.body.password, 10);
//save the user and checkfor errors
user.save(function(err) {
if (err) {
res.send(err);
} else {
res.json({message: "User created!"});
}
});
})
router.route('/users/:username')
.get(function(req, res) {
var query = {
username: req.params.username,
};
User.findOne(query, function(err, user) {
if (err) {
res.send(err);
} else {
bcrypt.compare(req.params.password, user.password, function(err, res) {
if(err) {
console.log('Comparison error: ', err);
}
})
res.json(user);
}
});
})
bcrypt.compare takes 3 parameters; passwordToCheck, passwordHash, and a callback, respectively. (Check the documentation for examples)
This error means one or both of the first 2 parameters are either null or undefined. Therefore, make sure both of them are passed correctly. (Not as null or undefined)
Why do we face this error?
bcrypt Error: data and hash arguments required
Example:
bcrypt.compare(first, second)
Ans:
because either second key hash password does not exist (null or undefined) or first, which are compared to each other.
I used
const user = await User.find({email: req.body.email}) //which returned all users
//and unless i reference the first user in index 0, i can't pass user.password to the //bcrypt compare method because it's not a string
I changed it to
await User.findOne({email: req.body.email})//from which i can use user.password in the //bcrypt compare method
const passwordMatch = await bcrypt.compare(password, user.password);
Make sure you are giving raw password and hash password. This will return a boolean value.
I was having the same error when I was working with node js and mongoose. It was caused by attribute added to password called select: false in user model.
After remove it, it works.
I had the same error and the problem was a missing await when calling the function that reads from database
the steps for this problem :
1-ensure that the bcrypt function is have awir before it
2- if the problem is still exist ,then the problem is in the database (mongodb),try to create new database
an example:
const match = await bcrypt.compare(password,userValid.password);
if (match) {
res.send("login successful")
}else{
res.send("wrong password")
}
}
I was having the same issue, but I was using the synchronous form of bycrypt.compare(), which is bcrypt.compareSync(), so I changed it to bcrypt.compare() and it works perfectly.
Use
findOne({})
instead of
find()
Try console.log() to view and verify the data.
try {
let match = await bcrypt.compare(password, user.password)
if(!match){
return res.json({mass: "invalid Created"})
}else{
res.send('Wrong password')
}
console.log('success fulli', user)
res.render('pages/auth/login', {title: 'Login In Your Account'})
} catch(e) {
console.log(e)
next(e)
}
The problem also can appear when you forget to add await when loading data from the database.
I got the same error after forgetting to add "await".
let user = User.findOne({ username: req.body.username });
let user = await User.findOne({ username: req.body.username });
I also have this problem i set for password select:false in user model and solved by adding select('+password') to login route
i know all the questions are solved but maybe someone finds this code works for him
const passwordMatch = await bcrypt.compare(password, user.rows[0].s_password);
The name after the dot it's the name you use in your database, it's the field