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

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.

Related

Handling Mongoose Query Errors in Express.js

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

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.

Cannot set headers after they are sent to the client error

I have a login form that authenticates using postgresql I'm trying to check if users exists then redirect the client to the other page. The code is:
app.post('/login', (req, res) => {
var Enteredusername = req.body.username;
var Enteredpassword = req.body.password;
pool.query("SELECT * FROM tbl_users WHERE username = $1 AND password = $2", [Enteredusername, Enteredpassword], (err, result) => {
if (err) return console.log('error in query', err);
// need to check if user exists
let user = (result.rows.length > 0) ? result.rows[0] : null;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}
res.redirect('/posts')
});
});
And I got the error:
_http_outgoing.js:470
throw new ERR_HTTP_HEADERS_SENT('set');
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
How Can I fix it?
It's the async behavior of javascript res.redirect('/posts') is executed before req.flash and res.render you can hack it like this :
req.session.userId = Enteredusername;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}else{
return res.redirect('/posts')
}

node js callback function issue

i am new to developing apis in node js. recently i started working on a node js application there i use jwt tokens to authentication purposes.
my jwt validation function is as bellow
var jwt = require('jsonwebtoken');
var config = require('../config.js')
var JwtValidations = {
//will validate the JTW token
JwtValidation: function(req, res, next, callback) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, callback);
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
}
module.exports = JwtValidations;
to this function i am passing a call back function so that if the jtw token validation passed i can serve to the request. bellow is one example of adding a user to the system
// addmin a user to the database
router.post('/add', function(req, res, next) {
JwtValidations.JwtValidation(req, res, next, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
retrunval = User.addUser(req.body);
if (retrunval === true) {
res.json({ message: "_successful", body: true });
} else {
res.json({ message: "_successful", body: false });
}
}
})
});
// addmin a user to the database
router.put('/edit', function(req, res, next) {
JwtValidations.JwtValidation(req, res, next, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
User.UpdateUser(req.body, function(err, rows) {
if (err) {
res.json({ message: "_err", body: err });
} else {
res.json({ message: "_successful", body: rows });
}
});
}
})
});
as you can see in both of these function i am repeating same code segment
return res.json({ success: false, message: 'Failed to authenticate token.' });
how do i avoid that and call the callback function if and only if JwtValidations.JwtValidation does not consists any error
how do i avoid that and call the callback function if and only if JwtValidations.JwtValidation does not consists any error
Just handle it at a level above the callback, either in JwtValidations.JwtValidation itself or a wrapper you put around the callback.
If you were doing it in JwtValidations.JwtValidation itself, you'd do this where you call the callback:
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
callback(decoded);
});
} else /* ... */
Now when you use it, you know either you'll get the callback with a successfully-decoded token, or you won't get a callback at all but an error response will have been sent for you:
router.put('/edit', function(req, res, next) {
JwtValidations.JwtValidation(req, res, next, function(decoded) {
User.UpdateUser(req.body, function(err, rows) {
if (err) {
res.json({ message: "_err", body: err });
} else {
res.json({ message: "_successful", body: rows });
}
});
})
});
The code above is using a lot of (old-style) NodeJS callbacks. That's absolutely fine, but you may find it's simpler to compose bits of code if you use promises instead. One of the useful things do is split the return path in two, one for normal resolution, one for errors (rejection).
Use the jwt authentication function as a middleware function and not as a route, plenty of examples on the express documentation.
http://expressjs.com/en/guide/using-middleware.html

404 ERROR(file not found) when trying to log to an authentication system built in express and node.js

I am making a demo banking app which supports user sign up and sign in using express.js and node.js.
The api built accepts POST requests to /signup and /authenticate routes when called via Postman but the /authenticate route gives a 404 error when called through $.ajax on the login form.
This is the jQuery ajax request in index.html
$.ajax({
url: '/authenticate',
method: 'POST',
data: cred,
dataType: 'json',
processData: false,
contentType: false,
success: function(success){
console.log(success);
},
error: function(err){
console.log(err);
}
})
server.js this is the server file
app.use(express.static(__dirname + '/../public'));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/../public/index.html'));
});
app.use('/api', api(passport));
This is app.js where routing is done
'use strict';
var router = require('express').Router();
var config = require('../config'),
allowOnly = require('../services/routesHelper').allowOnly,
AuthController = require('../controllers/authController'),
UserController = require('../controllers/userController'),
AdminController = require('../controllers/adminController');
var APIRoutes = function(passport) {
// POST Routes.
router.post('/signup', AuthController.signUp);
router.post('/authenticate', AuthController.authenticateUser);
// GET Routes.
router.get('/profile', passport.authenticate('jwt', { session: false }), allowOnly(config.accessLevels.user, UserController.index));
router.get('/admin', passport.authenticate('jwt', { session: false }), allowOnly(config.accessLevels.admin, AdminController.index));
return router;
};
module.exports = APIRoutes;
The POST request to /signup works but /authenticate gives a 404 error when using Ajax. But /authenticate works as expected when using Postman.
This is the authController.js
var jwt = require('jsonwebtoken');
var config = require('../config'),
db = require('../services/database'),
User = require('../models/user');
// The authentication controller.
var AuthController = {};
// Register a user.
AuthController.signUp = function(req, res) {
if(!req.body.username || !req.body.password) {
res.json({ message: 'Please provide a username and a password.' });
} else {
db.sync().then(function() {
var newUser = {
username: req.body.username,
password: req.body.password
};
return User.create(newUser).then(function() {
res.status(201).json({ message: 'Account created!' });
});
}).catch(function(error) {
console.log(error);
res.status(403).json({ message: 'Username already exists!' });
});
}
}
// Authenticate a user.
AuthController.authenticateUser = function (req, res) {
if (!req.body.username || !req.body.password) {
res.status(404).json({
message: 'Username and password are needed!'
});
} else {
var username = req.body.username,
password = req.body.password,
potentialUser = {
where: {
username: username
}
};
User.findOne(potentialUser).then(function (user) {
if (!user) {
res.status(404).json({
message: 'Authentication failed!'
});
} else {
user.comparePasswords(password, function (error, isMatch) {
if (isMatch && !error) {
var token = jwt.sign({
username: user.username
},
config.keys.secret, {
expiresIn: '30m'
}
);
res.json({
success: true,
token: 'JWT ' + token,
role: user.role
});
} else {
console.log("Log err")
res.status(404).json({
message: 'Login failed!'
});
}
});
}
}).catch(function (error) {
res.status(500).json({
message: 'There was an error!'
});
})
}
}
module.exports = AuthController;
Here's a log of the response.
POST /api/authenticate 404 5.092 ms - 47
Executing (default): SELECT id, username, password, role,
createdAt, updatedAt FROM users AS user WHERE
user.username = 'sipho' LIMIT 1;
POST /api/authenticate 200 519.020 ms - 193
I have tried everything. Please help because I am very new to node and Express.
Express responde automatically 404 when the path doesn't match with anyone declare. But in your case, I think is declared right.
As far as you are returning 404 code in several places, the way I'd check the error would be this:
Make sure with logs that the execution is entering in your method
Write different logs / console to know which 404 is being returned. Or you can return different http codes in every place in order to find which one is failing
After that you have to check why is entering in that condition and try to find the origin of the error.
For example. If the 404 returned is the first one, could be because in the ajax part you are not sending username or password. If it is second one, maybe because the username is not in registered or saved in your database.
Hope it helps

Categories