expressjs - response body not set - node.js

In the following NodeJS API, a PostgreSQL database is inserted with multiple rows and then a response body needs to be sent. Since there are multiple scenarios where there could be an error, I need to set-up response body with a message code that helps identify a possible cause. Therefore, I am using a resp object that will hold a code key for a message code. This object will be sent in a HTTP response where the status is either 200 (success) or 500 (error).
However, I keep getting an empty response body. Is the variable scope ok?
router.post('/', function (req, res, next) {
rdbmsPool.connect((err, client, release) => {
if (err) {
var resp = {
'Request-Id' : req.get('X-Request-Id'),
'code' : 'code'
}
res.status(500)
res.send(resp)
} else {
var resp = {}
for (k in someArray) {
client.query(queryString, colValues, (err, result) => {
if (err) {
resp = {
'Request-Id' : req.get('X-Request-Id'),
'code' : 'code'
}
res.status(500)
} else {
resp = {
'Request-Id' : req.get('X-Request-Id'),
'code' : 'code'
}
res.status(200)
}
})
}
for (k in someOtherArray) {
client.query(queryString, colValues, (err, result) => {
if (err) {
resp = {
'Request-Id' : req.get('X-Request-Id'),
'code' : 'code'
}
res.status(500)
} else {
resp = {
'Request-Id' : req.get('X-Request-Id'),
'code' : 'code'
}
res.status(200)
}
})
}
release()
res.send(resp)
}
})
});

Try something like this
let array1 = [];
for(k in someArray){
array1.push(client.query(queryString, colValues));
}
let array2 = [];
for (k in someOtherArray) {
array2.push(client.query(queryString, colValues));
}
try{
const results1 = await Promise.all(array1);
const results2 = await Promise.all(array2);
// if it comes here queries are successful
}
catch(error){
//send 500 here. any query have failed
console.log(error);
}

What happens is your response is sent before queries are completed. Your query execution is asynchronous and res.send(resp) does not wait until queries are executed.
The variable resp is set inside callbacks therefore it is empty.
You can try using async-await for synchronized execution with promise wrapper for parrelel execution of queries that are run inside loop. Note async function.
This is how it can be done with node mysql2/promise. Asynchronous means that the callback function you provide in query() will be called after query is executed. and the code after query() will run not waiting until query function is completed.
router.get('/getDayTypes',async function (req, res, next) {
const API_NAME='getDayTypes get, ';
try{
const conn = await pool.getConnection();
// Do something with the connection
var promise1 = conn.execute(`
SELECT
ID_DAY_TYPES,
DAY_NAME
FROM
hrm_calendar_day_types
WHERE
IS_ACTIVE = '1'`);
const values = await Promise.all([promise1]);
conn.release();
return res.status(200).send({success:true, data:values[0][0]});
}
catch(err){
logger.error(API_NAME + 'error :' + err);
return res.status(500).send({success:false, message:'Query Error'});
}
});

Related

weather app: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I am trying to build a weather app using node that takes
{
"cities": [
"toronto",
"mumbai",
"london"
]
}
as input and returns
{
"weather": {
"toronto": "24C",
"mumbai": "34C",
"london": "14C"
}
}
this as output
app.post('/getWeather',(req,res)=>{
const city = req.body.city;
city.map(city=>{
const url=`http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}`;
request(url, function(err, response, body) {
// On return, check the json data fetched
if (err) {
res.render('index', { weather: null, error: 'Error, please try again' });
} else {
let weather = JSON.parse(body);
console.log(weather);
Since the question does not have all of the code, I cannot give a definite answer, but I assume that the code either tries to sent response for the each city separately and/or does not wait for all the API calls to finish.
To fix the issue, async/await needs to be used (since the response depends on several API calls), and response must be sent after its completely assembled.
An example based on the given code:
app.post("/getWeather", async (req, res) => {
const cities = req.body.cities;
const reqs = cities.map((city) => {
const url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}`;
return new Promise((resolve, reject) => {
request(url, function (err, response, body) {
if (err) {
reject(err);
} else {
let weather = JSON.parse(body);
resolve(weather);
}
});
});
});
const responses = await Promise.all(reqs);
const result = {};
for (const response of responses) {
if (response.ok) {
result[response.city] = response.temp;
}
}
res.json(result);
});

Promise gets resolved on its own

The function gets resolved on its own and no data is returned from the function but null.
Can anyone solve this for me please?
print is happening like below :
[ RowDataPacket { payment_id: 'PAYID-MDRPLBY9LS10853614735924' } ]
data not 000000
data not 1111111
promise2
Get Payment Response
Payment Object
but it should be :
[ RowDataPacket { payment_id: 'PAYID-MDRPLBY9LS10853614735924' } ]
data not 000000
data not 1111111
Get Payment Response
Payment Object
promise2
async function func(req, res) {
return new Promise((resolve, reject) => {
let email_id = req.body.email_id;
let arr = [];
let ctr = 0;
sql_get_orderid =
"Select payment_id from paypal.execute_order where email_id=?";
db.query(sql_get_orderid, [email_id]).then(function (data) {
console.log(data);
if (data.length != 0) {
console.log("data not 000000");
data.forEach((x) => {
console.log("data not 1111111");
var paymentId = x.payment_id;
paypal.payment.get(paymentId, function (error, payment) {
if (error) {
console.log(error);
reject({ auth: true, message: "Error on the Server" });
} else {
console.log("Get Payment Response");
console.log(JSON.stringify(payment));
arr[ctr] = payment;
ctr = ctr + 1;
// resolve(payment);
// if(ctr==data.length)
}
});
});
resolve(arr);
}
});
});
}
router.post(
"/get_all_orders",
VerifyToken.Token_Verification,
async (req, res) => {
let arr = await func(req, res);
console.log("promise2");
res.send({ data: arr });
}
);
The reason why your logs are not in the order you expect it to be is that forEach in javascript expects a synchronous callback function.
It does not wait for promises or callbacks inside its callback to return.
Therfore in your code when func is invoked and once db.query is performed and if data.length is valid data not 000000 is logged.
Then iteration begins with forEach. Here for Each element in iteration data not 1111111 will be logged and an asynchronous call is made through paypal.paymeny.get method.
As mentioned above, forEach callbacks does not wait for it to get resolved and next iteration is continued and so on.
Once iteraions are done, resolve is invoked. By the time when resolve is invoked paypal.paymeny.get has not yet returned and thus empty array will be passed.
Then when the control is passed to the route handler promise2 is logged.
Get Payment Response is logged later once paypal.payment.get returns.
To avoid this, the execution sequence can be controlled with the help of promises. Please try with the below code.
async function func(req, res) {
try {
const email_id = req.body.email_id;
const sql_get_orderid =
"Select payment_id from paypal.execute_order where email_id=?";
const data = await db.query(sql_get_orderid, [email_id]);
if (!data.length)
return;
console.log('data not 000000');
const getPaymentPromises = data.map(x => new Promise((resolve, reject) => {
console.log("data not 1111111");
paypal.payment.get(x.payment_id, (error, payment) => {
if (error)
return reject(error);
console.log("Get Payment Response");
console.log(JSON.stringify(payment));
resolve(payment);
});
}));
return await Promise.all(getPaymentPromises);
} catch (error) {
return { auth: true, message: 'Error on the Server' };
}
}

Why I can not get correct response from api?

I can't get a good answer from my api , for example I try to read "true" or "false" from api to give the authorization for user , but I got just undefined data.
the methods both work prefectly from sending data to verify the user .
I have this api method inside server :
router.get("/sickers/user/login/:mail/:pass", (req, res) => {
//var values = JSON.parse(req.body);
var pass = req.params.pass;
var email = req.params.mail;
//console.log(values);
if (pass !== null || pass !== "") {
try {
con.connect();
con.query("SELECT Password FROM `sickers` WHERE Email='" + email + "'", function(err, rows, field) {
if (err) {
console.log(err);
res.send("an error detected try later");
} else {
try {
if (pass == rows[0].Password) {
res.json({ "result": "true" })
} else {
res.json({ "result": "false" })
}
} catch {
res.json({ "result": "false" })
}
}
});
} catch (e) {
res.send("no data found");
console.log("obj not found");
}
}
con.end();
});
and this call api inside my react app :
submithandler(e) {
e.preventDefault();
const url = 'http://localhost:8000/api/sickers/user/login/'+this.state.email+'/'+this.state.password+'';
const res = fetch(url);
const data = res;
this.setState({result:data});
alert(this.state.result);
}
thanks.
Account for the async nature of the functions you are using. It might look something like this:
const url = 'http://localhost:8000/api/sickers/user/login/'+this.state.email+'/'+this.state.password+'';
// Use .then to call a function AFTER the fetch has completed
fetch(url).then(result => result.json()).then((response) => {
// Use the setState callback to check updated values AFTER they have been updated
this.setState({result: response}, () => {
alert(this.state.result);
});
});
Docs on fetch
Docs on setState

What is the proper way to execute axios / firebase promises in a specific order in a firebase function?

What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch

How can I stack request calls so they don't complete until one is finished?

I have an array of items that I need to post to my server. I've tried the following but i never iterates.
var i = 0;
while (i < numOfItems) {
var item = items[i];
var a;
for(var ik in item){
console.log(item[ik]);
a = item[ik]; // Gets the key
break;
}
var formData = {
ID : ID,
UID : UID,
item : a
}
request.post({url:'http://example.com/a', formData: formData}, function(err, httpResponse, body){
if (err) {
return console.error('Post failed:', err);
}
console.log('Post successful! Server responded with:', body);
i++;
});
}
Your code won't work because request.post is asynchronous. If your objective is to make a call for each element in the array, a working and a more elegant solution would be to use Promises.all().
Here's your code modified with Promises —
function postRequest(url, formData) {
return new Promise((resolve, reject) => {
request.post({ url, formData }, function (err, httpResponse, body) {
if (!error) {
resolve({ message: 'Post successful!', response: body });
} else {
reject(err);
}
});
})
}
// Map your data array to an array of Promises
let promises = yourArray.map(element => {
let formData = {
ID: ID,
UID: UID,
item: element
}
return postRequest({ url: 'http://example.com/a', formData: formData })
});
// Wait for all Promises to complete
Promise.all(promises)
.then(results => {
// Handle results
})
.catch(e => {
// Handle error
});
A few things to note -
I'm reusing the fields ID and UID as-is, as it isn't clear where they come from in your code.
Replace yourArray with the array containing your data items.

Resources