res.end() gets executed before the promise finishes - node.js

My basic POST API makes a call to database , first to authenticate user and then to authentic if the products provided are true. But before productValidation can complete, res.end() is being executed.
As output, I get ::
"validated object"
"stopping the res"
"validated object"
Whereas I am trying to validated and add each cartItem to database and only then send a success response to client.
const express = require("express");
const app = express();
const router = express.Router();
var connection = require("./../database/serverConnector");
router.get("/:userId", (req, res, next) => {
connection.query(
"SELECT * FROM cart_items AS c INNER JOIN product AS p ON c.product_id = p.id WHERE user_id = ?",
[req.params.userId],
function (error, results, fields) {
if (error) throw error;
else {
console.log("Query made for cart of user " + req.params.userId);
res.status(200).send(results);
}
}
);
});
router.post("/:userId", userValidate, (req, res, next) => {
multipleProductValidation(req, res, next).then(function (data) {
res.end();
console.log("stopping the res");
});
// .catch(error){
// console.log(error);
// }
});
function userValidate(req, res, next) {
connection.query(
"SELECT * from user where id = ?",
[req.params.userId],
function (error, user, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
if (user.length == 0) {
res.status(404).json({
message: "User not found with ID " + req.params.userId,
});
} else next();
}
}
);
}
function multipleProductValidation(req, res, next) {
return new Promise(function (resolve, reject) {
req.body.cartItems.forEach((item, i) => {
productValidate(item, req, res, next).then(function (data) {
resolve(data);
console.log("validated object");
//next();
});
});
});
}
function productValidate(item, req, res, next) {
return new Promise(function (resolve, reject) {
connection.query(
"SELECT * from product where id = ?",
[item.product_id],
function (error, product, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
if (product.length == 0) {
res.status(404).json({
message: "Product not found with ID " + item.product_id,
});
resolve(false);
} else {
connection.query(
"INSERT INTO cart_items (product_id,user_id,size,quantity) VALUES (?,?,?,?)",
[item.product_id, req.params.userId, item.size, item.quantity],
function (error, results, fields) {
if (error) {
res.status(500).json({
message: error.sqlMessage,
});
} else {
res.statusText =
"Items added to cart for User " + req.params.userId;
resolve(true);
}
}
);
}
}
}
);
});
}
module.exports = router;

So what is happening here is that you are calling resolve in your multipleProductValidation before you finish processing all the products.
something like this should fix your problem
function multipleProductValidation(req, res, next){
return new Promise(function(resolve, reject) {
let promiseArr = req.body.cartItems.map(item => productValidate(item, req, res, next))
Promise.all(promiseArr).then(values => resolve(values))
}
)}

Related

requires a callback function but got a [object Undefined]

I am creating a RESTful API using Node.js , express and My sql. I have created 2 Services,2Routers, and 2 Controllers
App.js module
require("dotenv").config();
const express = require("express");
const app = express();
const userRouter = require("./api/Routers/user.router");
const categoryRouter=require("./api/Routers/category.router");
app.use(express.json());
app.use("/api/users", userRouter);
app.use("/api/category", categoryRouter);
const port = process.env.PORT ;
app.listen(port, () => {
console.log("server up and running on PORT :", port);
});
Category.service.js
const pool = require("../../config/database");
module.exports = {
create: (data, callBack) => {
pool.query(
`insert into category(name, , branch, state, create_date, modified_date)
values(?,?,?,?,?,?)`,
[
data.name,
data.branch,
data.state,
data.create_date,
data.modified_date,
],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results);
}
);
},
getUserByUserEmail: (email, callBack) => {
pool.query(
`select * from registration where email = ?`,
[email],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
getCategoryByCategoryId: (id, callBack) => {
pool.query(
`select id,name,branch,state,create_date,modified_date from category where id = ?`,
[id],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
getCategory: callBack => {
pool.query(
`select id,name,branch,state,create_date,modified_date from category`,
[],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results);
}
);
},
updateCategory: (data, callBack) => {
pool.query(
`update category set name=?, branch=?, state=?, create_date=?, modified_date=?, `,
[
data.name,
data.branch,
data.state,
data.create_date,
data.modified_date,
],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
},
deleteCategory: (data, callBack) => {
pool.query(
`delete from category where id = ?`,
[data.id],
(error, results, fields) => {
if (error) {
callBack(error);
}
return callBack(null, results[0]);
}
);
}
};
Category.route.js
const router = require("express").Router();
const { checkToken } = require("../../auth/token_validation");
const {
createCategory,
login,
getCreateByCreateId,
getCategory,
updateCategory,
deleteCategory
} = require("../Controllers/category.controller");
router.get("/", checkToken, getCategory);
router.post("/", checkToken, createCategory);
router.get("/:id", checkToken, getCreateByCreateId);
router.post("/login", login);
router.patch("/", checkToken, updateCategory);
router.delete("/", checkToken, deleteCategory);
module.exports = router;
Category.controll.js
const {
create,
getUserByUserEmail,
getCategoryByCategoryId,
getCategory,
updateCategory,
deleteCategory
} = require("../services/category.service");
const { hashSync, genSaltSync, compareSync } = require("bcrypt");
const { sign } = require("jsonwebtoken");
module.exports = {
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
body.password = hashSync(body.password, salt);
create(body, (err, results) => {
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection error"
});
}
return res.status(200).json({
success: 1,
data: results
});
});
},
login: (req, res) => {
const body = req.body;
getUserByUserEmail(body.email, (err, results) => {
if (err) {
console.log(err);
}
if (!results) {
return res.json({
success: 0,
data: "Invalid email or password"
});
}
const result = compareSync(body.password, results.password);
if (result) {
results.password = undefined;
const jsontoken = sign({ result: results }, process.env.JWT_KEY, {
expiresIn: "1h"
});
return res.json({
success: 1,
message: "login successfully",
token: jsontoken
});
} else {
return res.json({
success: 0,
data: "Invalid email or password"
});
}
});
},
getCategoryByCategoryId: (req, res) => {
const id = req.params.id;
getCategoryByCategoryId(id, (err, results) => {
if (err) {
console.log(err);
return;
}
if (!results) {
return res.json({
success: 0,
message: "Record not Found"
});
}
results.password = undefined;
return res.json({
success: 1,
data: results
});
});
},
getCategory: (req, res) => {
getCategory((err, results) => {
if (err) {
console.log(err);
return;
}
return res.json({
success: 1,
data: results
});
});
},
updateCategory: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
body.password = hashSync(body.password, salt);
updateCategory(body, (err, results) => {
if (err) {
console.log(err);
return;
}
return res.json({
success: 1,
message: "updated successfully"
});
});
},
deleteCategory: (req, res) => {
const data = req.body;
deleteCategory(data, (err, results) => {
console.log(results);
if (err) {
return;
}
if (!results) {
return res.json({
success: 0,
message: "Record Not Found",
h:results
});
}
return res.json({
success: 1,
message: "category deleted not successful"
});
});
}
};
I am getting Error: Route.post() requires a callback function but got a [object Undefined. how can i sort it out
Error: Route.post() requires a callback function but got a [object Undefined] how can I sort this error
createUser should be renamed in createCategory in Category.controll.js

Rejected Promise Resolves in NodeJs

I'm not able to catch the rejected promise and I don't understand where I'm going wrong. Here's what I have
exports.signIn = (username, password) => {
return new Promise((resolve, reject) => {
pool.query(
"select * from user where username=? order by id asc limit 1",
[username],
(err, result, fields) => {
if (!err) {
console.log("user result: ", result);
if (result.length === 1) {
let user = result[0];
bcrypt.compare(password, user.password, (error, res) => {
if (error) {
reject(error);
}
if (res) {
console.log("user found: ",user.username);
resolve(user);
} else {
console.log("Incorrect password");
reject("Unauthorized Access");
}
});
} else {
console.log("user not found");
reject("Invalid username");
}
}
}
);
});
};
This is how I use the promise
app.post("/signin", (req, res, next) => {
let body = req.body;
let password = body.password;
let username = body.username;
db.signIn(username, password)
.catch(err => {
res.status(200).json({ err });
})
.then(result => {
console.log("signin: ", result);
res.status(200).json({ result });
});
});
When I enter a correct password, it resolves properly but when I enter a wrong password it still resolves with the signin console message and an UnhandledPromiseRejectionWarning warning. I really don't see where I'm going wrong, perhaps an extra eye will do.
You should use promise as :
app.post("/signin", (req, res, next) => {
let body = req.body;
let password = body.password;
let username = body.username;
db.signIn(username, password)
.then(result => {
console.log("signin: ", result);
res.status(200).json({ result });
}).catch(err => {
res.status(200).json({ err });
});
});
Because, after catch if you will add any number of then handling, it will execute them all.

Express: how to end response

I new in Nodejs and faced some problems and need help
I broke down my app into modules and controllers
in app.js
app.use('/api/sync', syncDataRouter)
and in syncDataRouter.js
const routes = function (con, Reading, ReadingSummary) {
const syncDataRouter = express.Router();
const syncDataController = require('../controller/syncDataController')(con, Reading, ReadingSummary);
//Get All Readings from Database and push it to Mongo-db
syncDataRouter.route('/')
.get(syncDataController.sync);
return syncDataRouter;
};
module.exports = routes;
and in controller
const syncDataController = function (con, Reading, ReadingSummary) {
function getAllCompletedSessions() {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM session WHERE endTime IS NOT NULL';
con.query(sql, (err, rows) => {
if (err) reject(new Error(err));
resolve(rows);
});
});
};
function getSessionPlayers(sessionId) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM sessionplayers WHERE sessionId = ?';
con.query(sql, [sessionId], (err, rows) => {
if (err) reject(new Error(err));
resolve(rows);
});
});
};
const sync = (req, res) => {
getAllCompletedSessions()
.then(sessions => {
})
.catch(err => {
res.status(500).send({ message: err.message });
});
//The Problem
res.json('Done');
};
return {
sync: sync
}
}
module.exports = syncDataController;
the problem is if any error happened enter a catch block and also continue until res.json('Done'); and sent a message can't set headers after they are sent how to handle a situation like that
Try making the sql queries async
const syncDataController = function (con, Reading, ReadingSummary) {
async function getAllCompletedSessions() {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM session WHERE endTime IS NOT NULL';
await con.query(sql, (err, rows) => {
if (err) reject(new Error(err));
resolve(rows);
});
});
};
async function getSessionPlayers(sessionId) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM sessionplayers WHERE sessionId = ?';
await con.query(sql, [sessionId], (err, rows) => {
if (err) reject(new Error(err));
resolve(rows);
});
});
};
const sync = (req, res) => {
getAllCompletedSessions()
.then(sessions => {
})
.catch(err => {
res.status(500).send({ message: err.message });
});
//The Problem
res.json('Done');
};
return {
sync: sync
}
}
module.exports = syncDataController;

Impelement promise in JS

I have a function located in /models/profile.js:
module.exports = {
getDataProfil : function(id_user){
connection.query("SELECT * FROM user WHERE id_user = ?", [id_user], function(err, rows, fields){
if(err) throw err;
console.log(rows[0]);
});
}
}
I want to call the getDataProfile inside /controllers/profile.js
module.exports.profil_get = function(req, res, next) {
profile_model.getDataProfil(req.user.id_user).then();
}
I want to implement promise there, since I need to render the page. How to do it? Thankyou
Use this, You can also use q module too.
module.exports = {
getDataProfil: function(id_user) {
connection.query("SELECT * FROM user WHERE id_user = ?", [id_user], function(err, rows, fields) {
if (err) return ['err', err];
return [null, rows[0]];
});
}
}
And profil_get will be:
module.exports.profil_get = function(req, res, next) {
profile_model.getDataProfil(req.user.id_user)
.then(function(res) {
console.log(res);
});
}
Check if res[0] is null for success or is a string for rejection.
Another implementation is
module.exports = {
getDataProfil: function(id_user) {
var dfd = q.defer();
connection.query("SELECT * FROM user WHERE id_user = ?", [id_user], function(err, rows, fields) {
if (err) { dfd.reject(err); }
else { dfd.resolve(rows[0]); }
});
}
return dfd.promise;
}
And profil_get:
module.exports.profil_get = function(req, res, next) {
profile_model.getDataProfil(req.user.id_user)
.then(function(res) {
console.log(res);
})
.catch(function(err){
console.log(err);
});
}
Using native promises:
module.exports = {
getDataProfile : function ( id_user ) {
return new Promise( ( resolve, reject ) => {
connection.query( 'SELECT * FROM user WHERE id_user = ?', [ id_user ], function ( err, rows, fields ) {
if ( err ) {
return reject( err );
}
return resolve( rows[ 0 ] );
} );
} );
}
};
You can then call your promise and handle response/error cases. Ideally, you would respond to a client instead of using console.log/error:
module.exports.profile_get = function(req, res, next) {
profile_model.getDataProfile(req.user.id_user).then( response => {
console.log( response );
}).catch( err => {
console.error( err );
});
};
Just use native Promise constructor:
module.exports = {
getDataProfil : function(id_user) {
return new Promise(function (resolve, reject) {
connection.query("SELECT * FROM user WHERE id_user = ?", [id_user], function (err, rows, fields) {
if (err) return reject(err);
return resolve(rows[0]);
});
})
}
}

the variable i is not filled

I have declare the variable first. but if I do console.log(userinsertData) outside looping variable still not fill.
what i should do for solving this problem?
here my code:
var User = require('../models/user');
module.exports = {
myaction: function(req, res, next) {
var data = req.body,
userinsertData = [];
try {
data.forEach(function(item, index) {
var userdata = new User();
userdata.name = item.name;
userdata.age = item.age;
userdata.sex = item.sex;
userdata.save(function(err, data) {
if (err) {
res.send(err)
} else {
userinsertData.push(data);
}
});
})
} catch (e) {
res.json({
message: 'data not valid'
})
}
console.log(userinsertData);
res.json({
message: 'musician created!',
data: userinsertData
});
}
};
you should solve the problem as
async.eachSeries(data, function (info, callback) {
//here process your data and call callback() for next iteration
}, function (err) {
if (err) {
//this will be called after all iterations and in case of error
}else{
console.log('Well done :-!');
//this will be called after all interations successfully
}
});
this problem you are facing is because of asynchronous nature of nodejs and async helps you to introduce blocking.
Don't forget to include async
Use promise
var User = require('../models/user');
module.exports = {
myaction: function(req, res, next) {
var data = req.body,
userinsertData = [];
new Promise(function(resolve, reject) {
data.forEach(function(item, index) {
var userData = new User(item);
userData.save(function(err, data) {
// if error, reject
if(err) return reject(err);
// we have proceed all items in data, resolve it
else if(data.length - 1 === index) return resolve(userinsertData);
// not finished yet, keep proceeding
else userinsertData.push(data);
});
}).then(function(successResult) {
res.json({
message: 'musician created!',
data: successResult
});
}, function(errorResult) {
res.json({
message: 'data not valid'
});
});
}
};
Use callbacks
var User = require('../models/user');
module.exports = {
myaction: function(req, res, next) {
var data = req.body,
userinsertData = [];
function saveUser(callback) {
data.forEach(function(item, index) {
var userData = new User(item);
userData.save(function(err, data) {
// if error, return callback with error
if(err) return callback(err);
// we have proceed all items in data, return data
else if(data.length - 1 === index) callback(null, userinsertData);
// not finished yet, keep proceeding
else userinsertData.push(data);
});
}
saveUser(function(err, users) {
if(err) return res.json({message: 'data not valid'});
res.json({
message: 'musician created!',
data: users
});
});
}
};
This is what async package does internally

Resources