Express.js: Reponse in catch of try/catch is never sent - mysql

I'm trying to implement a basic user registration flow using Express.js and mySQL. This is the code I have at the moment (stripped for brevity):
register(app, db) {
app.post('/register', (req, res) => {
let email = req.body.email
let password = req.body.password
try {
// add the user to the database
var q_params = [email, bcrypt.hashSync(password, 9)]
db.query("INSERT INTO users VALUES (?, ?)", q_params, (err) => {
if (err) {
throw err
}
})
} catch (err) {
// respond with an error if one is encountered
res.json({
success: false,
msg: err
})
return
}
// respond with success if everything else goes ok
res.json({
success: true,
email: email
})
});
}
The problem is that no matter the outcome of the code in the try block, I am always getting success: true. How come the error response is never triggered? Is there a better way to be handling this scenario?

I'm assuming the issue is that the nothing is waiting for the db.query() function to finish executing. It's not async and not awaiting that function to resolve. So the last res.json({}) gets hit right away.
I would try to rewrite the code to either use promises or to use that callback function passed to .query(), maybe something like this:
app.post('/register', (req, res) => {
let email = req.body.email
let password = req.body.password
// add the user to the database
var q_params = [email, bcrypt.hashSync(password, 9)]
db.query("INSERT INTO users VALUES (?, ?)", q_params, (err) => {
if (err) {
return res.json({
success: false,
msg: err
});
}
res.json({
success: true,
email: email
});
});
});

Related

TypeError : res.status is not a function returning Login message and Token

i am getting this as error
TypeError : res.status is not a function
I do not know why, but i am getting this Error
My code is looking thus :
app.post('/api/v1/user/login', async function(req,res){
var email = req.body.email;
var password = req.body.password;
dbConn.query(`SELECT * FROM XXXXXXXXX_users WHERE email = ${dbConn.escape(req.body.email)};`,(err,result)=>{
if(err){
throw err;
return res.status(400).send({message: err,})
}
if(!result.length){
return res.status(400).send({message: 'Username and password incorrect!',})
}
bcrypt.compare(req.body.password,result[0]['password'],(err,res)=>{
if(err){
throw err;
return res.status(400).send({message: 'Username and password Incorrect!'});
}
if(result){
const token = jwt.sign({email: result[0].email,id:result[0].id},'the-super-strong-secrect',{ expiresIn: '1h' });
return res.status(200).send({message: 'OK', token}) // Error is here
}
return res.status(400).send({message: 'User and pass incorrect'})
})
})
})
I am trying to implement Login system backend API please I would like to know why this is not working as its supposed to. Kindly help. A bit new to this
Issue is in this line
bcrypt.compare(req.body.password,result[0]['password'],(err,res) //<-- Using res keyword here
See how you are using (err, res) for this callback. This interferes with
app.post('/api/v1/user/login', async function(req,res) //<-- Also using res keyword here
I would suggest you to use async/await for better cleanup.
app.post('/api/v1/user/login', async function(req, res) {
let email = req.body.email;
let password = req.body.password;
try {
const queryResult = await dbConn.query(`SELECT * FROM XXXXXXXXX_users WHERE email = ${dbConn.escape(req.body.email)};`)
if (!queryResult.length) {
return res.status(400).send({
message: 'Username and password incorrect!',
})
}
const compareResult = await bcrypt.compare(req.body.password, queryResult[0]['password'])
if (compareResult) {
const token = jwt.sign({
email: queryResult[0].email,
id: queryResult[0].id
}, 'the-super-strong-secrect', {
expiresIn: '1h'
});
return res.status(200).send({
message: 'OK',
token
})
}
return res.status(400).send({
message: 'User and pass incorrect'
})
}
catch (err) {
res.status(500).json({
err
});
}

User always getting failure redirected using passport?

No matter what I change the user login will keep redirecting to failure instead of success. I don't know if I'm missing something or if I did something wrong. I tried to read the documentation for passport but, I found it pretty confusing. Here is my github link if you need to see the rest of the code. The node files are in app.js and passport-config.js.The sign up part of the website is working. https://github.com/gego144/to-do-list-website/tree/main
const customFields = {
usernameField: 'email',
passwordField: 'password'
}
const verifyCallback = (username, password, done) => {
user_exists = userName_Checker(username), function (err, user) {
if (err) { return done(err); }
if (userName_Checker(username) == false) {
console.log('wrong user');
return done(null, false, { message: 'Incorrect username.' });
}
if (password_finder(username, password)) {
console.log('wrong pass');
return done(null, false, { message: 'Incorrect password.' });
}
console.log('wtf');
return done(null, user);
};
;
}
const strategy = new LocalStrategy(customFields, verifyCallback);
passport.use(strategy);
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// function that checks to see if the users email is in the database
function userName_Checker(email_name){
var sql = "select * from info where email = ?";
var user_email = [[email_name]];
db.query(sql, [user_email],function (err,result){
if (err) throw err;
var not_unique = result.length;
if(not_unique == 0){
return false;
}
else{
return true;
}
}
)}
// function that checks to see if the password in the database matches with the email
function password_finder(email_name, pass){
var sql = "SELECT password FROM info WHERE email = ?";
var user_email = [[email_name]];
db.query(sql, [user_email],function (err,result){
if (err) throw err;
bcrypt.compare(result, pass, function(err, res){
if(err){ throw err};
if(res){
return true;
}
else{
return false;
}
})
}
)}
My post method in my other file.
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect:'/index.html',
failureFlash: true
}))
Edit 1.
I just want to mention that the console.logs you see in verify Callback all don't log anything for some reason too.
The problem might be in the serialization logic.
In passport.serializeUser, you are passing in the whole user object, but when deserializing you are passing the id
Though I am not using SQL, the logic should be similar.
So the code should be something like this:
// Session
// Pass in user id => keep the session data small
passport.serializeUser((id, done) => {
done(null, id);
});
// Deserialize when needed by querying the DB for full user details
passport.deserializeUser(async (id, done) => {
try {
const user = await User_DB.findById(id);
done(null, user);
} catch (err) {
console.error(`Error Deserializing User: ${id}: ${err}`);
}
});
// Export the passport module
module.exports = (passport) => {
passport.use(new LocalStrategy({ usernameField: 'email', }, async (email, password, done) => {
try {
// Lookup the user
const userData = await User_DB.findOne({ email: email, }, {
password: 1, }); // Return the password hash only instead of the whole user object
// If the user does not exist
if (!userData) {
return done(null, false);
}
// Hash the password and compare it to the hash in the database
const passMatch = await bcrypt.compare(password, userData.password);
// If the password hash does not match
if (!passMatch) {
return done(null, false);
}
// Otherwise return the user id
return done(null, userData.id);
} catch (err) {
passLog.error(`Login Error: ${err}`);
}
}));
};
These options for passport seems to malfunction a lot or exhibit weird behaviors, so I suggest you handle the redirection logic like in my controller.
{ successRedirect: '/good',
failureRedirect: '/bad' }
Login controller logic:
(I am omitting the code here for session storage and made some modifications, but this code should work for what you need)
const login = (req, res, next) => {
//Using passport-local
passport.authenticate('local', async (err, user) => {
//If user object does not exist => login failed
if (!user) { return res.redirect('/unauthorized'); }
//If all good, log the dude in
req.logIn(user, (err) => {
if (err) { return res.status(401).json({ msg: 'Login Error', }); }
// Send response to the frontend
return res.redirect('/good');
});
});
})(req, res, next);
};
The actual route:
// Import the controller
const {login} = require('../controllers/auth');
// Use it in the route
router.post('/auth/login', login);

Stop another .then() execution in chain of promises

I have programme in nodejs & mysql like below
db.query()
.then(1)
.then(2)
.then(3)
.catch()
I am checking a value from database in then(1) and trying to return response from there.In then(2) , I am executing another code that uses some data from result of then(1) and so on..
My problem: When returning response from then(1), catch() is calling(because then(2) have error, not getting data from then(1)) . So is there any way I can stop further execution in then(1) so that then(2) and catch() couldn't call ?
db.query('query......', [val1, val2])
.then(rslt => { return res.json({ mssg: "Email already exists!", error: "Email already exists!" }) })
.then(user => { return db.query('INSERT INTO ', value, (err, res, flds) => { err ? reject(err) : resolve(res) }) })
.then(user => { return res.json({ mssg: "Success", success: true}) })
.catch( (err) => { console.log(err) })
You can (and should) use an async function, instead of using the lower-level .then() API of the Promise object:
async function doTheThing() {
try {
const result = await db.query('...');
if (result) { // user exists
return res.json({...}); // this will end the entire function
}
const user = await db.query('...');
return res.json({...}); // success
} catch (err) {
console.log(err); // I don't recommend doing this. try/catch should be for recovery
}
}

nodejs- unable to return result to controller function

From my Model, I fetch some articles from a MySQL database for a user.
Model
var mysql = require('mysql');
var db = mysql.createPool({
host: 'localhost',
user: 'sampleUser',
password: '',
database: 'sampleDB'
});
fetchArticles: function (user, callback) {
var params = [user.userId];
var query = `SELECT * FROM articles WHERE userId = ? LOCK IN SHARE MODE`;
db.getConnection(function (err, connection) {
if (err) {
throw err;
}
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
return connection.query(query, params, function (err, result) {
if (err) {
connection.rollback(function () {
throw err;
});
}
//console.log(result);
});
});
});
}
This is working and the function fetches the result needed. But it's not returning the result to the controller function (I am returning it but I'm not able to fetch it in the controller function. I guess, I did something wrong here).
When I did console.log(result) this is what I got.
[ RowDataPacket {
status: 'New',
article_code: 13362,
created_date: 2017-10-22T00:30:00.000Z,
type: 'ebook'} ]
My controller function looks like this:
var Articles = require('../models/Articles');
exports.getArticle = function (req, res) {
var articleId = req.body.articleId;
var article = {
userId: userId
};
Articles.fetchArticles(article, function (err, rows) {
if (err) {
res.json({ success: false, message: 'no data found' });
}
else {
res.json({ success: true, articles: rows });
}
});
};
Can anyone help me figure out what mistakes I made here?
I'm pretty new to nodejs. Thanks!
The simple answer is that you're not calling the callback function, anywhere.
Here's the adjusted code:
fetchArticles: function (user, callback) {
var params = [user.userId];
var query = `SELECT * FROM articles WHERE userId = ? LOCK IN SHARE MODE`;
db.getConnection(function (err, connection) {
if (err) {
// An error. Ensure `callback` gets called with the error argument.
return callback(err);
}
connection.beginTransaction(function (err) {
if (err) {
// An error. Ensure `callback` gets called with the error argument.
return callback(err);
}
return connection.query(query, params, function (err, result) {
if (err) {
// An error.
// Rollback
connection.rollback(function () {
// Once the rollback finished, ensure `callback` gets called
// with the error argument.
return callback(err);
});
} else {
// Query success. Call `callback` with results and `null` for error.
//console.log(result);
return callback(null, result);
}
});
});
});
}
There's no point in throwing errors inside the callbacks on the connection methods, since these functions are async.
Ensure you pass the error to the callback instead, and stop execution (using the return statement).
One more thing, without knowing the full requirements of this:
I'm not sure you need transactions for just fetching data from the database, without modifying it; so you can just do the query() and skip on using any beginTransaction(), rollback() and commit() calls.

MEAN Stack app - Can't set headers after they are sent

I'm basically trying to create a register page using MEAN and I want to check against the database (Mongo using Mongoose) if the user and email already exists, if one of them is true, send a res.json fail.
And if the username and email are not already in the database, continue to add the user.
I'm getting an error "Can't set headers after they are sent." On the Node console and I'm trying to figure out why.
Once User.getUsername() returns a user in the callback (if the username passed exists in the db), shouldn't it return the json and end the request there? Why is it continuing to the User.adduser() function and trying to set the header there too?
Any help is appreciated, thank you.
router.post('/register', (req, res, next) =>{
let newUser = new User({ // Collect body info
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
// Check if username is available
User.getUserByUsername(newUser.username, (err, user) => {
if(err) throw err;
if(user){
return res.json({success: false, msg: 'User already exists'});
// I want to end here if there's a user
}
});
//Continue to add user if getUserByUsername() returns false for user
// Add user
User.addUser(newUser, (err, user) => { // Add user
if(err){
return res.json({success: false, msg: 'Failed to register'});
} else {
return res.json({success: true, msg: 'You have been successfully registered!'});
}
});
});
The logic you have doesn't work for as expected because of how asynchronous methods work. The current logic is do User.getUserByUsername then do User.addUser and if either method calls back, handle it. I'm pretty sure you want, do User.getUserByUsername, wait for it's callback, then call User.addUser if necessary. Here's a crude implementation, you could use promises or define the methods outside of the logic to clean it up. Also, make sure you are calling res.end() at some point in your code.
router.post('/register', (req, res, next) =>{
let newUser = new User({ // Collect body info
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
// Check if username is available
User.getUserByUsername(newUser.username, (err, user) => {
if(err) throw err;
if(user){
return res.json({success: false, msg: 'User already exists'});
// I want to end here if there's a user
}else{
//Continue to add user if getUserByUsername() returns false for user
// Add user
User.addUser(newUser, (err, user) => { // Add user
if(err){
return res.json({success: false, msg: 'Failed to register'});
} else {
return res.json({success: true, msg: 'You have been successfully registered!'});
}
});
}
});
});