Nodejs: Can't set headers after they are sent - node.js

This node/express function is giving me an error:
Can't set headers after they are sent.
it used to work fine, but i have made some changes to the user schema, moving all address items to be under 'address', like this:
firstname,
lastname,
address:{
street,
city,
loc (array of numbers)
...
}
so the new function looks like this:
export function searchMembers(req, res) {
var lat = req.body.lat;
var lon = req.body.lon;
var zoom = req.body.zoom || 14;
var query = User.find();
var distance = 5000;
// when this line is removed, problem is gone:
query = query.where('address.loc').near({center:{type:'Point', coordinates:[lon,lat]}, maxDistance: distance, spherical:true});
query = query.where({'address.city': 'Toronto'});
query = query.sort({'lastname': 1});
query.exec(function(err,users){
if(err) res.send(err);
var final = [];
_.forEach(users, function(x){
var obj = {};
obj.id = x._id;
obj.name=x.firstname + ' ' + x.lastname;
obj.latitude=x.address.loc[1] ;
obj.longitude=x.address.loc[0] ;
final.push(obj);
});
res.status(200).json(final);
});
}
So when i run this, i get the funny error: Can't set headers after they are sent.
pointing to the last line in the function:
res.status(200).json(final);
i tried to eliminate stuff to find the root cause.
when i remove the where line with the 'near' function, the problem is gone.
i have added other filtering, just for testing, everything fine. only this one is causing an issue.
Any idea?

This error means, that you already used method res.json()/render()/send() and you try to do it again.
In your case, this line does not stop method from executing if(err) res.send(err);
You have to write return to stop it.
if(err) {
res.send(err);
return;
}
Which is equivalent to
if(err) {
return res.send(err);
}
Just do not think about it as returning "res.send(err)", it is using res.send(err) and after that using return to stop executing.
The reason why removing line also removes error :
You have some error in that line (like having bad column names), therefore in callback the error is send and then you use res.send(err) and after that you call res.status(200).json(final)

Related

How to execute a stored procedure in node js+firebird

I'm using node.js 6.9.0 and "firebird": "^0.1.3" connecting to a firebird 3. Currently, I have several queries that are working perfectly just calling in the form 'select * from ...' then I decided to create a stored procedure and I'm having 2 problems.
The connexion.query is no longer an FBResult object(thenFunc) to apply the fetch that was giving me an error,
When I don't treat the result as an FBResult because is already a JSON string the received values are always null, However, I receive the correct column names.
The initial call was like this:
var sql = "EXECUTE PROCEDURE PRESENCE_GETTOTAL;"
var resultVec = [];
connexion.query(sql , function (err,rs){
if( rs == null && err != null ){
return reject(err);
}
else{
console.log(rs);
rs.fetch('all',true,function (rs){
ShopDayString.push(rs);
}, function (err, oef){
if(oef) resolve(ShopDayString);
if(err) reject(err);
});
Then I realized it was giving back in rs the result as a JSON so I change it this way:
var sql = "EXECUTE PROCEDURE PRESENCE_GETTOTAL;"
var resultVec = [];
console.log(sql)
connexion.query(sql , function (err,rs){
if ( rs == null && err != null ){
return reject(err);
}
else{
console.log(rs);
resultVec.push(rs);
resolve(resultVec)
}
});
The problem now is that the values are always null, I run the exact same command in the isql-fb client and I got results. Does anyone have any idea why this is happening?
The correct way to execute a stored procedure is like this:
var stmt = connexion.prepareSync("EXECUTE PROCEDURE PRESENCE_GETTOTAL;")
var res = stmt.execSync();
if(res){
resolve(res);
}
else{
res.fetch('all',true,function (rs){
resultVec.push(rs);
}, function (err, oef){
if(oef) resolve(resultVec);
if(err) reject(err);
});
}
So when we execute the Sync there is 2 option, If it is a single row it will immediately return the result. Otherwise, it will return undefined and you will have to fetch the results.
You can get more info in the library repository
edit 1: cleaner code suggested not using 'undefined'.

In Node/Express, how to match or compare values in nested promises using csv-to-array

I have a Node/Express partial that is being called with AJAX, and is supposed to send a status update back to the view, after 2 subsequent API calls are made. This workflow relies on the csv-to-array module to read a ship-orders.csv file, and determine if the second API call (POST to Shipments) has already occured. It is supposed to do this by matching the OrderNumber in the csv file to the returned OrderNumber from the FindOrders endpoint (the first API).
The problem is that I am creating 2 arrays of order numbers to compare, but matching the first set of order numbers to the second set either always returns true or always returns false, and it very clearly should show "true" for the first record in the csv, and "false" for every other.
Before getting into the bulk of the code, here's the promise that reads the csv file into an array:
csv-to-array:
var csvShipPromise = new Promise(function(resolve, reject){
var csvColumns = ['ChannelName', 'OrderNumber', 'LineNumber', 'WarehouseCode', 'Qty', 'Carrier', 'TrackingNumber', 'Shipdate', 'ShipMethod'];
var csvShipArr;
var csvArr;
csvArray({
file: shipLog,
columns: csvColumns
}, function(err, array){
csvShipArr = array;
resolve(csvShipArr);
});
});
Next I have a long promise that gets executed when the request to the partial is made. The comparison between logged OrderNumbers and OrderNumbers that need to be posted to Shipments is the 5th "then" block (and it's commented in the code below).
router.get and chained promise:
router.get('/', function(req, res, next) {
findPromise.then(function(findData){
//Properly format xml string
var foundData = replaceAll(findData, '<', '<');
foundData = replaceAll(foundData, '>', '>');
return foundData;
}).then(function(foundData){
//Parse xml to JSON and stringify
var parsedFound;
parseString(foundData, function(err, result){ //uses an xml to json module
parsedFound = JSON.stringify(result);
});
return(parsedFound);
}).then(function(parsedStr){
//Parse JSON and return an array of objects
var parsedJson = JSON.parse(parsedStr);
var orders = parsedJson['soap:Envelope']['soap:Body'][0]['FindOrders'][0]['orders'][0]['order'];
return orders;
}).then(function(orders){
//Get only orders with a tracking number.
var trackArray = [];
var ord;
for(ord in orders){
var postObj = orders[ord];
if(postObj.TrackingNumber[0].length > 1){
trackArray.push(postObj);
}
}
return trackArray; //array of orders that contain tracking numbers
}).then(function(trackArray){
/**** This is the block that is causing problems. *****/
var tItm;
var loggedOrders = [];
for(tItm in trackArray){
var alreadyLogged = false;
var trackedItm = trackArray[tItm];
var trackedOrderNum = trackedItm.ReferenceNum;
csvShipPromise.then(function(csvOrders){
var csv;
var loggedOrderArr = [];
for (csv in csvOrders){
var csvItm = csvOrders[csv];
var csvOrderNum = csvItm.OrderNumber; //gets the OrderNumber as expected
loggedOrderArr.push(csvOrderNum);
}
return loggedOrderArr; //Return a simple array of all OrderNumbers
}).then(function(loggedOrderArr){
console.log(loggedOrderArr);
console.log(trackedOrderNum);
var ord;
for (ord in loggedOrderArr){
if(trackedOrderNum == loggedOrderArr[ord]){
console.log('found');
alreadyLogged = true;
}
else {
console.log('not found');
alreadyLogged = false;
}
}
return loggedOrderArr; //Simply returning this value because the alreadyLogged test isn't working.
});
/* Here is where the test fails.
It shouldn't, because there are, say, 4 OrderNumbers in the result of the first API call,
and only 1 Order number logged in the CSV.
So it should be true once, and false 3 times.
But it is true all the time.
*/
if(alreadyLogged){
console.log('found'); //Always logs true/found.
} else {
console.log('not found');
}
}
return trackArray; //Just passing the array to the view, for now.
}).then(function(obj){
res.send(obj);
return(obj);
}).catch(function(err){
console.log(err);
});
});
When I console.log the values of trackArray and loggedOrderArr, I see that there should be an intersection between an array of 4 values and an array of 1 value, but for some reason the comparison, if(trackedOrderNumber == loggedOrderArr[ord]) isn't working.
Alright, I'm gonna be honest, your code made my eyes swim. but as far as I can tell, a few things pop up:
move var alreadyLogged = false; to before the loop;
then add alreadyLogged = false; after if(alreadyLogged) statement
I think it has to do with scope. You are basically checking bool value of a var that has not changed yet because your promises has not resolved at the point of if(alreadyLogged)
Might I suggest a different approach?
why not make use of array.indexOf() ?
lets say you have two arrays to compare arrA & arrB; you can see if an item exists like so:
var index = arrA.indexOf(arrB[0]);
if(index == -1){
console.log('No Match');
}
else{
console.log('Match found');
}
no need for any preset flags to see if one array contains an element.
Hope it helps.
A bit more context:
var index = loggedOrderArray.indexOf(trackedOrderNum);
if(index == -1){
console.log('No Match');
// -1 basicaly means that there is not instance of trackedOrderNum in loggedOrderArray
}
else{
console.log('Match found');
}
What you are attempting appears to be reasonably simple. You are just overwhelming yourself with awkward flow control and bulky code.
As it stands, asynchronous flow isn't quite right chiefly due to parseString() not being promisified. A value returned from a raw nodeback won't propagate down a .then chain.
In addition, asynchronous flow will improve with :
application of Promise.all() up front to aggregate the two essential data-delivering promises csvShipPromise and findPromise.
the realisation that wholly synchronous steps in a promise chain can be merged with next step.
And, the bulk of the synchronous code will reduce by employing several Array methods:
Array.prototype.filter()
Array.prototype.map()
Array.prototype.includes()
Boiling it down to somewhere near the bare minimum, I get the following router.get() expression:
router.get('/', function(req, res, next) {
return Promise.all([csvShipPromise, findPromise])
.then([csvOrders, findData] => { // destructuring
let loggedOrderArr = csvOrders.map(order => order.OrderNumber);
let foundData = replaceAll(findData, '<', '<');
foundData = replaceAll(foundData, '>', '>');
return new Promise((resolve, reject) => { // promisify parseString() on the fly
parseString(foundData, (err, result) => {
if(err) reject(err);
else resolve(result['soap:Envelope']['soap:Body'][0].FindOrders[0].orders[0].order); // does this expression really return `orders` (plural)?
});
})
.then(orders => {
let trackArray = orders.filter(postObj => postObj.TrackingNumber[0].length > 1); // filter orders to eliminate those without a tracking number.
let loggedOrders = trackArray.filter(trackedItm => loggedOrderArr.includes(trackedItm.ReferenceNum));
// let unloggedOrders = trackArray.filter(trackedItm => !loggedOrderArr.includes(trackedItm.ReferenceNum));
res.send(loggedOrders); // or res.send(unloggedOrders), depending on what you want.
});
})
.catch(err => {
console.log(err);
res.error(err); // or similar
});
});
untested - I may have made mistakes, though hopefully ones that are simple to correct

MongoDB returning a null, but query works separately

In a post function, I am trying to retrieve the nth activity of a user (since I have a dropdown that return the index number of the activity). When I run the query
collection.find({'local.email':req.user.local.email},
{'local.activities':{$slice : [currActivity,1]}});
I receive the correct activity object in Robo3T.
But, when I call the same query in Node inside a post function, it returns an undefined.
app.post('/addlog',function(req,res){
var currActivity = req.body.curAct;
var score = req.body.score;
var comment = req.body.reason;
mongoose.connect('mongodb://****:****#ds044907.mlab.com:44907/intraspect',function (err, database) {
if (err)
throw err
else
{
db = database;
var collection = db.collection('users');
var retrievedAct = collection.find({'local.email':req.user.local.email},
{'local.activities':{$slice : [currActivity,1]}}).toArray().then(console.log(retrievedAct));
if (retrievedAct.length > 0) { printjson (retrievedAct[0]); }
console.log(currActivity);
console.log(retrievedAct[0]);
// console.log(req.body.newAct);
collection.update({'local.activities.name':retrievedAct[0]},
{$push: {'local.activities.log' : {
comments: comment,
score: score,
log_time: Date.now()
}}})
.then(function(){
res.redirect('/homepage');
})
.catch(function() {
console.log('Error');
});
}
});
});
I checked that the currActivity variable does infact contain the integer value for the nth activity.
If you want the result of collection.find().toArray(), as specified in the docs, you have two options:
Passing a callback to .toArray() like you did with mongoose.connect()
Using the Promise that it returns if you don't pass a callback
Now you are doing neither of them.
Also, you are mixing callback style and Promises in your code. I recommend you unificate your code. If you are using a Node.js version bigger than 8, using async/await could be nice, it makes it simpler.

Error after using request parameters in function

I’m getting an error when trying to fetch data with the instagram-node package while using request parameters.
Making the call without parameters but hard coded values works without any errors and gives me the right result.
When I use the parameters, as shown below, I get the following error:
{ [Error: Wrong params "lat" & "lng"] retry: [Function] }
This is my code:
//http://localhost:8080/photos/2000/52.3677985/4.8852246
app.get('/photos/:dist/:longitude/:latitude', function(req,res) {
var dist = req.params.dist;
var longitude = req.params.longitude;
var latitude = req.params.latitude;
console.log(dist + " " + longitude + " " + latitude);
ig.media_search(longitude, latitude, {distance: dist},
function (err, medias, remaining, limit) {
if (err) {
console.log(err);
} else {
res.render('pages/index', {grams: medias});
}
});
Logging the parameters gives me the required values, but they don’t seem to be defined when making the ig.media_search() call.
Am I missing something here?
The first thing that I'm seeing is that you're sending in latitude and longitude in backwards. The signature is:
media_search = function(lat, lng, options, cb) {}
Secondly, this is the line that is throwing your error from instagram-node:
if(typeof lat !== 'number' || typeof lng !== 'number') {
return handle_error(new Error('Wrong params "lat" & "lng"'), cb, retry);
}
I'm going to guess that you're sending in string values that have the numbers you want as they were parsed out of the url string. If you try calling it like this, you might see better results:
//http://localhost:8080/photos/2000/52.3677985/4.8852246
app.get('/photos/:dist/:longitude/:latitude', function(req,res) {
// Cast as numbers to send into instagram.
var dist = Number(req.params.dist);
var longitude = Number(req.params.longitude);
var latitude = Number(req.params.latitude);
console.log(dist + " " + longitude + " " + latitude);
ig.media_search(latitude, longitude, {distance: dist},
function (err, medias, remaining, limit) {
if (err) {
console.log(err);
} else {
res.render('pages/index', {grams: medias});
}
});
By the way, I've never used this package before. If you go from the npm page for this package you can find a link to the github repo for it. If you look at their package.json you can find a main value that will lead you to the entry point for the package. In that file (lib/instagram.js) you can Ctrl+F for media_search and find the function in you're calling. Just thought you might want to know how to go through and debug something like this. :)

return value is null in node.js with mongoose

I am using node.js with mongoose. The problem i am facing is i am getting newModifier1 printed but outside that function the value is null.
Here is my code:
// Find userSchema
newModifier1 = "";
exports.findModifier = function(modifierName){
modifierModel.find({'name' : modifierName},function(err,result){
if(err){
console.log("Error : "+err);
throw err;
}
else{
newModifier1 = result;
// console.log("Modifier is searched successfully : "+newModifier1);
}
console.log("Modifier is searched successfully1 : "+newModifier1);
});
// newModifier1=temp;
return newModifier1; // it takes newModifier1 = "" value here
}
Any ideas what the problem could be?
This is what is happening:
// this is "global" an would be weirdly overwritten
// if function is called multiple times before finishing
newModifier1 = "";
exports.findModifier = function(modifierName){
// TIMESTAMP: 0
modifierModel.find({'name' : modifierName},function(err,result){
// TIMESTAMP: 2
if(err){
console.log("Error : "+err);
throw err;
}
else{
newModifier1 = result;
// console.log("Modifier is searched successfully : "+newModifier1);
}
console.log("Modifier is searched successfully1 : "+newModifier1);
});
// TIMESTAMP: 1
return newModifier1; // it takes newModifier1 = "" value here
}
I added some notes, when what is happening. As you can see and because of the async nature of node.js you return the value before you get a result back from the database.
You need familiarize yourself with the async flow and callback function.
Pass a callback function to findModifier and wait for the database to return a result.
modifierModel.find runs asynchronously and probably findModifier method is returning before the callback of find method executes. Although you see it being printed out what is returned from the method is en empty string anyway. You can use a library like async.

Resources