Can't set headers after they are sent error with async - node.js

I'm trying a pretty complex computation in my code below. I'm trying to get the list of bugs from github in the given project using the api https://api.github.com/repos/marklogic/java-client-api/issues?page=1&per_page=10. From the list of bugs I'm trying to get each issues' corresponding events and comments from their corresponding endpoints ex: https://api.github.com/repos/marklogic/java-client-api/issues/291/events and https://api.github.com/repos/marklogic/java-client-api/issues/291/comments.
I'm using async library. I'm using waterfall function and parallel function to return a consolidated JSON for each bug such that each issue will have comment, & events in the same response for each issue. The problem is its throwing Can't set headers after they are sent error & its pointing to line 2 lines, I understand what the error is saying but I can't figure out how to fix it, because commenting out either of the offending lines results in request hang because the server is not sending the response. Please help! Thanks in advance
exports.listGitHubBugs = function(req, res) {
var _page = req.query.page || 1;
var _per_page = req.query.per_page || 25;
var finalResult = []
//console.log('url:', 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page);
var options = {
url: 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page,
headers: {
'User-Agent': req.query.project
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log(error);
res.send(error);
}
if (!error && response.statusCode === 200) {
var issues = JSON.parse(response.body)
async.waterfall([
// get comments & events for all bugs and then send the response
function(callback) {
issues.forEach(function(issue) {
// for each bug, get comments and events
async.parallel([
function(parallelCallback) {
var options = {
url: issue.events_url,
headers: {
'User-Agent': getProjectNameFromURL(issue.events_url)
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log('ERROR', error);
parallelCallback(error)
}
if (!error && response.statusCode === 200) {
// console.log('events:', body);
parallelCallback(null, body)
}
})
},
function(parallelCallback) {
var options = {
url: issue.comments_url,
headers: {
'User-Agent': getProjectNameFromURL(issue.comments_url)
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log('ERROR', error);
parallelCallback(error)
}
if (!error && response.statusCode === 200) {
// console.log('comments:', body);
parallelCallback(null, body)
}
})
}
], function(err, result) {
if (err) {
console.log('ERROR:', err);
callback(err);
}
console.log('parallel process done');
issue.events = JSON.parse(result[0]);
issue.comments = JSON.parse(result[1]);
finalResult.push(issue)
callback(null, finalResult) // offending line#1
})
}) // forEach end
}
], function(err, result) {
if (err) {
res.send(err);
}
console.log('waterfall done');
console.log(result);
res.send(result); // offending line#2
})
} // if end
}) // reqest end
}
Error
UncaughtException: Can't set headers after they are sent.
ERROR Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
at ServerResponse.res.set.res.header (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:524:10)
at ServerResponse.res.send (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:125:10)
at ServerResponse.res.json (/Users/sreddy/space/angularjs/BugTrack/node_modules/express/lib/response.js:191:15)
at /Users/sreddy/space/angularjs/BugTrack/server/api/common/common.controller.js:163:33
at /Users/sreddy/space/angularjs/BugTrack/server/api/common/common.controller.js:153:29
at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:254:17
at done (/Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:135:19)
at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:32:16
at /Users/sreddy/space/angularjs/BugTrack/node_modules/async/lib/async.js:251:21
Final working code
exports.listGitHubBugs = function(req, res) {
var _page = req.query.page || 1;
var _per_page = req.query.per_page || 25;
var finalResult = []
//console.log('url:', 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page);
var options = {
url: 'https://api.github.com/repos/marklogic/' + req.query.project + '/issues?page=' + _page + '&per_page=' + _per_page,
headers: {
'User-Agent': req.query.project
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log(error);
return res.send(error);
}
if (!error && response.statusCode === 200) {
var issues = JSON.parse(response.body)
async.waterfall([
// get events and comments for all bugs and return the final processes list of bugs
function getEventsAndCommentsForAllBugs(callback) {
issues.forEach(function getEventsAndComments(issue, index) {
// for each bug, get comments and events
async.parallel([
function getEvents(parallelCallback) {
var options = {
url: issue.events_url,
headers: {
'User-Agent': getProjectNameFromURL(issue.events_url)
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log('ERROR', error);
parallelCallback(error)
}
if (response.statusCode === 200) {
// console.log('events:', body);
parallelCallback(null, body)
}
})
},
function getComments(parallelCallback) {
var options = {
url: issue.comments_url,
headers: {
'User-Agent': getProjectNameFromURL(issue.comments_url)
},
auth: githubAuth
};
request(options, function(error, response, body) {
if (error) {
console.log('ERROR', error);
parallelCallback(error)
}
if (response.statusCode === 200) {
// console.log('comments:', body);
parallelCallback(null, body)
}
})
}
], function attachEventsAndComments(err, result) {
if (err) {
console.log('ERROR:', err);
callback(err);
}
console.log('parallel process done');
issue.eventList = JSON.parse(result[0]);
issue.commentList= JSON.parse(result[1]);
finalResult.push(issue)
if (index === (issues.length - 1)) {
callback(null, finalResult)
}
//
})
}) // forEach end
}
], function processedBugs(err, result) {
if (err) {
res.send(err);
}
console.log('waterfall done');
console.log(result);
res.send(result);
})
} // if end
}) // reqest end
}

could you provide a complete working example of the code, something we can try.
this said, there are several errors in this source code.
Onthe first request, if an error occurs, you write it in app.response,, but you don t stop execution. Thus, if an error occurs, you ll write twice the response object.
You should do
if (error) {
console.log(error);
return res.send(error);
}
instead of
if (error) {
console.log(error);
res.send(error);
}
Then, this can be changed
if (!error && response.statusCode === 200) {
to
if (response.statusCode === 200) {
Same mistake occurs while fetching issues events and comments, please consider to fix it.
And also in the final callback of async.//
And in the final callback of async.waterfall.
finally, i suggest you to make use of named functions. That would help you to debug by providing more meaningfull error stack trace.
For example instead of doing,
async.prallel([function(){/* code here*/}]);
You would write
async.parallel([function nameOfTheTask(){/* code here*/}]);
consider also to use a linter such as eslint, several missing ; could break your code, see http://eslint.org/

I figured it out. I was calling the parellelCallback() for every iteration instead of calling it at the end of the loop. Simple if condition was all that was required.
if (index === (issues.length-1)) {
callback(null, finalResult)
}

Related

Callback-return value doesn't get stored in a HTTP-request (NodeJS)

I have set up a server-application with NodeJS/Express and am trying to get the following script to work so that it stores the return value of a http-request in a variable for later use.
function checkIfVerified(req, res) {
var user_id = req.user.id
var options = {
method: 'GET',
url: 'https://MYDOMAIN/api/v2/users/' + user_id + '?fields=email_verified&include_fields=true',
headers:
{
authorization: 'Bearer ' + app.locals.token
},
body:
{
},
json: true
}
function check(callback) {
request(options, function (error, response, body) {
return callback(null, body)
})
}
var email_verified = check(function (err, data) {
if (!err) {
return data
}
})
console.log(email_verified)
return email_verified
}
For some reason the variable 'email_verified' doesn't hold any value ...
Thanks for your help!
Cheers
Philip
So after #Derlins clue I found out the following solution to the question:
function check() {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) {
reject(error)
} else {
resolve(body.email_verified)
}
})
})
}
res.locals.email_verified = await check()
This does the trick :-)

reCaptcha back-end nodejs

I simply want to verify a reCAPTCHA using NodeJS and am having trouble making the simple call!
I keep getting errors missing-input-response and missing-input-secret.
Attempt 1 using request:
var request = require('request');
...
request.post(
'https://www.google.com/recaptcha/api/siteverify',
{
secret: 'MY_SECRET',
response: recaptcha
},
function (error, response, body) {
// guard
if (error) {
callback(false);
return;
}
if (response.statusCode == 200) {
console.log("BODY", body)
if (body.success) {
callback(true);
} else {
callback(false);
}
}
}
Attempt 2 using https:
var post_req;
var requestBody = {
secret: 'MY_SECRET',
response: recaptcha
};
post_req = https.request('https://www.google.com/recaptcha/api/siteverify', function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('CHUNK: ', chunk);
});
});
post_req.on('error', function (e) {
console.log('ERROR: ', e);
callback(false);
});
post_req.write(requestBody);
post_req.end();
The result is:
{
"success": false,
"error-codes": [
"missing-input-response",
"missing-input-secret"
]
}
Finally found a solution. It seems the issue was with the Content-Type.
This works:
var request = require('request');
...
request.post(
'https://www.google.com/recaptcha/api/siteverify',
{
form: {
secret: 'MY_SECRET',
response: recaptcha
}
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body)
}
}
);

How to pass variables to request callback without using a forloop?

For example if I like to do:
var request = require('request');
function callback(error, response, body) {
if (num) {
console.log(num);
}
console.log(body);
}
var cookie = {
'74358942795': 'abd7fce573c2-18c2c0de-037f-4aef-9235',
'58942743795': 'e3101e9a0e28-b596998e-e879-4003-a724'
}
function options(num) {
return {
url: 'http://.../.../',
method: 'POST',
headers: {
...
'Cookie': cookie[num]
},
body: '...=' + Date.now()
};
}
(function(num){
request(options(num), callback);
})('74358942795');
Here within the callback it will return num undefined and throw an error.
Igor's answer is almost right. It should be:
function callback(error, response, body) {
var that = this;
if (that.num) {
console.log(that.num);
}
console.log(body);
}
(function(num){
request(options(num), callback.bind({num}));
})('74358942795');
num isn't available to the callback scope. Simplest solution I see would be to replace that last block with:
(function(num){
request(options(num), function (error, response, body){
if (num) {
console.log(num);
}
console.log(body);
});
})('74358942795');
I don't get how "forloop" is related to the code in question. Use bind:
function callback(num, error, response, body) {
if (num) {
console.log(num);
}
console.log(body);
}
(function(num){
request(options(num), callback.bind(null, num));
})('74358942795');
function callback(num, error, response, body) {
if (num) {
console.log(num);
}
}
(function(num) {
setTimeout(callback.bind(null, num), 1000);
})('74358942795');

How to requests-request_id-receipt-get?

I'm trying to get the receipt but I can't!
I already set "request_receipt" in AUTHORIZATIONS on my app and nothing change.
What I have to do to get de receipt?
<script>
var headers = {
'Accept-Language':'en_US',
'Content-Type':'application/json'
};
function callback(error, response, body) {
if(error){
console.log(error);
}else if (response.statusCode == 200) {
var jsonBody = JSON.parse(body);
headers['Authorization'] = `Bearer ${jsonBody.access_token}`;
getUserHistory(req,res);
}
}
request.post(options, callback);
});
function getUserHistory(req,res){
var options = {
url: 'https://api.uber.com/v1.2/history',
headers:headers
};
request.get(options, (error, response, body) => {
if(error){
console.log(error);
}else if (response.statusCode == 200) {
var bodyJson = JSON.parse(body);
bodyJson.history.map(function(model){
var options = {
url: 'https://api.uber.com/v1.2/requests/'+model.request_id+'/receipt',
headers:headers
};
request.get(options, (error, response, body) => {
if(error){
console.log(error);
}else if (response.statusCode == 200) {
res.send(body);
}
});
})
}
});
}
</script>
Running this code, I can see this message:

Why resolve is undefined within callback of request?

When i run code given below in terminal than i get following error:-
ReferenceError: resolve is not defined.
const request = require('request');
let geoLocationPromise = (zipCode) => {
return new Promise(()=>{
request({
url:`https://maps.google.com/maps/api/geocode/json?address=${zipCode}`,
JSON: true
}, (error, response, body)=>{
if(error){
reject('Unable to connect to server');
}else if (response.statusCode === 200) {
console.log(body);
resolve(JSON.parse(body.currently, undefined, 2));
}
});
});
};
geoLocationPromise(841101).then((loc)=>{
console.log(loc);
}, (errorMessage)=>{
console.log(errorMessage);
});
You need to declare the parameters “reject” and “resolve” for your Promise's callback, like this:
const request = require('request');
let geoLocationPromise = (zipCode) => {
return new Promise((resolve, reject)=>{
request({
url:`https://maps.google.com/maps/api/geocode/json?address=${zipCode}`,
JSON: true
}, (error, response, body)=>{
if(error){
reject('Unable to connect to server');
}else if (response.statusCode === 200) {
console.log(body);
resolve(JSON.parse(body.currently, undefined, 2));
}
});
});
};

Resources