Handling Mongoose Query Errors in Express.js - javascript

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"
}
})
})

Related

How can I catch Error from Node.js user Model

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,
);

Cannot set headers after they are sent to the client using nodejs mysql

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.

Node js MongoDB login system database values undefined

I am currently working on a login system with Nodejs, Express & MongoDB. Everything works except the values in the database are coming up as undefined. At the two console.log statements where "database ___" is stated, the result is undefined. Not too sure why, from some testing it seems that the user inputted values work fine so I don't know why it's returning undefined.
app.post("/login", (req, res) => {
//Get user fields
const userEmail = req.body.loginEmail;
const userPass = req.body.loginPassword;
//Is user in database?
User.find({ email: userEmail }, (err, user) => {
console.log("database email: " + user.email)
if (!err) {
//Compare password to database password
bcrypt.compare(userPass, user.password, (err, result) => {
console.log("database password: " + user.password);
//If user pass in database, check if verified & redirect to success
if (userPass === user.password) {
if (user.isVerified) {
res.redirect("/success");
} else {
res.send(
"You are not verified. Please check your email to access your account."
);
}
} else {
res.send("Incorrect password");
}
});
} else {
res.send(err);
}
});
});
Mongoose will return an array as the second argument to the callback function when you use find(). If you use findOne() a single document will be returned instead.

bcrypt Error: data and hash arguments required

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

ReactJS + MongoDB + NodeJS/ExpressJS: What is process.nextTick(function() { throw err; });?

In my ReactJS project, I am currently running the server with NodeJS and ExpressJS, and connecting to the MongoDB using MongoClient. I have a login API endpoint set up that accepts a request with user's username and password. And if a user is not found, should catch the error and respond with an error (status(500)) to the front-end.
But rather than responding to the front-end with an json error, the server gets crashed. I have tried everything to figure out why but still no luck.
How can I fix the following error? Any guidance or insight would be greatly appreciated, and will upvote and accept the answer.
I intentionally made a request with a username and a password ({ username: 'iopsert', password: 'vser'}) that does not exist in the database.
Here is the login endpoint:
//login endpoint
app.post('/api/login/', function(req, res) {
console.log('Req body in login ', req.body)
console.log('THIS IS WHAT WAS PASSED IN+++++', req._id)
db.collection('users').findOne({username: req.body.username}, function(err, user) {
console.log('User found ')
if(err) {
console.log('THIS IS ERROR RESPONSE')
// Would like to send this json as an error response to the front-end
res.status(500).send({
error: 'This is error response',
success: false,
})
}
if(user.password === req.body.password) {
console.log('Username and password are correct')
res.status(500).send({
username: req.body.username,
success: true,
user: user,
})
} else {
res.status(500).send({
error: 'Credentials are wrong',
success: false,
})
}
})
And here is the terminal error log:
Req body in login { username: 'iopsert', password: 'vset' }
THIS IS WHAT WAS PASSED IN+++++ undefined
User found
/Users/John/practice-project/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
TypeError: Cannot read property 'password' of null
at /Users/John/practice-project/server/server.js:58:12
at handleCallback (/Users/John/practice-project/node_modules/mongodb/lib/utils.js:96:12)
at /Users/John/practice-project/node_modules/mongodb/lib/collection.js:1395:5
at handleCallback (/Users/John/practice-project/node_modules/mongodb/lib/utils.js:96:12)
at /Users/John/practice-project/node_modules/mongodb/lib/cursor.js:675:5
at handleCallback (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:165:5)
at setCursorNotified (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:505:3)
at /Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:578:16
at queryCallback (/Users/John/practice-project/node_modules/mongodb-core/lib/cursor.js:226:18)
at /Users/John/practice-project/node_modules/mongodb-core/lib/connection/pool.js:430:18
And /Users/John/practice-project/node_modules/mongodb/lib/utils.js:98 is referring to the following:
var handleCallback = function(callback, err, value1, value2) {
try {
if(callback == null) return;
if(value2) return callback(err, value1, value2);
return callback(err, value1);
} catch(err) {
process.nextTick(function() { throw err; });
return false;
}
return true;
}
EDIT
Here are everything that's being imported to the server:
"use strict"
var express = require('express');
var path = require('path');
var config = require('../webpack.config.js');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var bodyParser = require('body-parser');
var MongoClient = require('mongodb').MongoClient;
var ObjectId = require('mongodb').ObjectID;
const jwt = require('jsonwebtoken')
var app = express();
var db;
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('dist'));
app.use(bodyParser.json());
And this is how the request is made and error is caught:
loginUser(creds) {
var request = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(creds),
}
fetch(`http://localhost:3000/api/login`, request)
.then(res => res.json())
.then(user => {
console.log(user);
console.log('Successful')
})
.catch(err => {
console.log('Error is', err)
})
},
It looks to me like the error is being thrown on this line because user is not defined.
if(user.password === req.body.password) {...}
Take a harder look at your console statements.
1. Req body in login { username: 'iopsert', password: 'vset' }
2. THIS IS WHAT WAS PASSED IN+++++ undefined
3. User found
4. /Users/John/practice-project/node_modules/mongodb/lib/utils.js:98
5. process.nextTick(function() { throw err; });
^
6. TypeError: Cannot read property 'password' of null
7. at /Users/John/practice-project/server/server.js:58:12
Line 2 shows that req._id is undefined
Your User found statement is printed before you check if there is an error or if the user actually exists, so it isn't representative of there actually being a user.
Line 6 shows that the error is being thrown because you're trying to read a property of password from a null object.
I'd recommend modifying your login logic to look more like this:
//login endpoint
app.post('/api/login/', function(req, res) {
console.log('Performing login with req.body=');
console.log(JSON.stringify(req.body, null, 4));
// check for username
if (!req.body.username) {
return res.status(401).send({message: 'No username'});
}
// find user with username
db.collection('users').findOne({username: req.body.username}, function(err, user) {
// handle error
if(err) {
console.log('Error finding user.');
return res.status(500).send({message: 'Error finding user.'});
}
// check for user
if (!user) {
console.log('No user.');
return res.status(500).send({message: 'No user.'});
}
console.log('User found.');
// check password
if(user.password !== req.body.password) {
console.log('Wrong password.');
return res.status(401).send({message: 'Wrong password.'});
}
// return user info
return res.status(200).send(user);
});
Some final thoughts:
Make sure to handle the error (if it exists) and check that user exists before proceeding.
Always include return in your return res.status(...).send(...) statements, otherwise the subsequent code will execute.
It's generally not a good idea to save passwords as simple strings. Work toward encrypting them. Look at passport or bcrypt.
Hope this helps.

Categories