I am using the got library to make some http requests. A typical got request looks like this:
got.post(url, options, function(err, body, res) {
...
}
The callback has errors as well as the body content and the full response. In some cases when connecting to certain 3rd party APIs, if I get an error response, I find both the err and body both contain important error information I would want to pass back up the chain of callbacks:
API.prototype.makeRequest = function(callback) {
got.post(url, {}, function(err, body, res) {
if (err) {
callback(err);
return false;
}
// Otherwise do stuff
});
}
// app.js
var myAPI = new API();
myAPI.makeRequest(function(err, data){
if (err) {
console.log(err);
return false;
}
// Do stuff...
});
So in this example, I'm obviously only returning the error for the response. An example of this error:
{ [HTTPError: Response code 400 (Bad Request)]
message: 'Response code 400 (Bad Request)',
code: undefined,
host: 'api.someservice.com',
hostname: 'api.someservice.com',
method: 'POST',
path: '/oauth2/token_access',
statusCode: 400,
statusMessage: 'Bad Request' }
But if I examine the body of the error request, I see even more important and relevant error information:
{ error_description: 'Grant type is not supported: some_type_of_grant', error: 'unsupported_grant_type' }
So I have useful information in both the err and body variables that I want to pass back up the callback chain since both are useful.
What is the best approach to this? Typical Node.js approach seems to be having a single err value be the first value in the callback. Should I combine them into a single object or combine them and formulate my own error? Any other good approaches?
Related
Using axios, I'm making GET requests to multiple links to test to see if they are broken links. If the GET requests returns an error, I want to log this error to the console and send it to the client side with socket. However, on some links (not all), I get an UnhandledPromiseRejectionWarning.
// check if the link is dead
axios.get(linkHref, {
auth: {
username: USERNAME,
password: PASSWORD
}
})
.then( (response) => {
if (response.status != 200) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(response.status)
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
})
.catch( (error) => {
if (error) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(error.response.status) // this is where the error is
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
});
I expect it work correctly and return the status code of the error but sometimes I get a UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'status' of undefined error. But with my if statement in the .catch, I am checking to make sure that there is an error and that it is not undefined so I don't understand why I'm getting this error.
EDIT: Also, this works (does not show me that error) when I run it on my Mac but not in Windows ... why?
TypeError: Cannot read property 'status' of undefined The response property on error.response is not defined eg error = { response: undefined }. Therefore, referencing any properties on it throws an error. error.response[property]
Also on failed/hanging http requests the error argument is different. Checkout this gist axios-catch-error. The error argument will have different properties for this case.
hey can you check by validating whether you are getting the response property on the error object in the if else condition
.catch( (error) => {
if (error.response) {
//rest of the code...
}
});
I am using ftp library on npm. I am facing this error
_http_outgoing.js:357
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:357:11)
at ServerResponse.header (/Users/nomanmaqsood/Documents/netsuite-ftp/node_modules/express/lib/response.js:730:10)
First time I get proper response from the library on second call my node app crash due to the above error. Here is my code kindly guide me where is the mistake
c.on ('ready', function () {
c.list (function (err, list) {
if (err) {
c.end();
} else {
if (list.length > 0) {
c.end();
return res.status(200).json({data:list});//crash here on 2nd call
}
}
});
});
please help me out
Check that you don't call res.json or res.send twice on one client http request.
Try
c.on ('ready', function () {
c.list (function (err, list) {
if (err)
return res.status(500).json({error: err.message});
c.end();
res.status(200).json({data: list || []});
});
});
P.S. I suppose that you use Express.
the c was global object, which register itself twice. just make the object local and things works like charm
Situation
From my Meteor.js website I'm calling my own REST service. Here's a code sample from my server side
function (question) {
var r = Async.runSync(function (done) {
HTTP.get(URL, {
params: {q: question}, headers: {
"Accept": "application/json",
}
}, function (err, result) {
done(err, result);
});
});
if (r.err) {
console.log("Failed to smartSearch ... ", r.err);
return null;
} else if (r.result.content) {
console.log("Success ... ");
return JSON.parse(r.result.content);
}
}
This works great but there is also some crucial information in the response headers which I'm unable to find.
What I've tried so far
I viewed everything within r.result.content, but this only contains my request headers.
I've installed https://atmospherejs.com/gadicohen/headers and tried everything the site said.
But still not seeing my response headers.
Additional Information
I'm fairly new to Meteor.js so I don't really have an idea what I might be doing wrong but getting response headers doesn't see like a strange thing to me.
There is no need to wrap the request as an async call, as it already is.
You can use a try..catch block to handle both successful and failed requests.
try {
var result = HTTP.get(...);
var responseHeaders = result.headers;
} catch (e) {
// handle error
}
If the response headers indicate JSON response, it will be parsed and available as result.data. The response will be available as a string in result.content.
More details are available in the HTTP package API docs.
I am writing an updated testing library for Node.js and am trying to properly trap errors that occur in test callbacks
for some reason, the following code doesn't trap an AssertionError:
process.on('uncaughtException',function(err){
console.error(err); //an instance of AssertionError will show up here
});
[file1,file2,file2].forEach(function (file) {
self.it('[test] ' + path.basename(file), {
parallel:true
},function testCallback(done) {
var jsonDataForEnrichment = require(file);
request({
url: serverEndpoint,
json: true,
body: jsonDataForEnrichment,
method: 'POST'
}, function (error, response, body) {
if (error) {
done(error);
}
else {
assert(response.statusCode == 201, "Error: Response Code"); //this throws an error, which is OK of course
done();
}
});
});
});
I handle the callback (I named it "testCallback" above), with this code:
try {
if (!inDebugMode) {
var err = new Error('timed out - ' + test.cb);
var timer = setTimeout(function () {
test.timedOut = true;
cb(err);
}, 5000);
}
test.cb.apply({
data: test.data,
desc: test.desc,
testId: test.testId
}, [function (err) { //this anonymous function is passed as the done functon
cb(err);
}]);
}
catch (err) { //assertion error is not caught here
console.log(err.stack);
cb(err);
}
I assume the problem is that callbacks that result from async functions like those made in the request module, cannot be trapped by simple error handling.
What is the best way to trap that error?
Should I just flesh out the process.on('uncaughtException') handler? Or is there a better way?
The best way to handle this appears to be Node.js domains, a core module
https://nodejs.org/api/domain.html
it will likely be deprecated soon, but hopefully there will be a replacement that can have similar functionality, because the domain module is saving my ass right now, as I have no other way to trap errors, because the errors might be generated by my users' code, not my code.
I have a NodeJS server and a helper function that is making a HTTP request, but when I make the call for the function, it comes up as undefined. The call is being made in a callback, so I don't think that there is a problem with the Async part of it.
This is the server.js
console.log(dictionary('wut'));
And this is the function dictionary.
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
return info.list[0].definition;
}
});
} else {
return dictionary[word];
}
Where word is the word that is being passed to the function.
Edit: I forgot to mention that the dictionary function has
module.exports = function(word){
The return statements are supposed to give the module the value from the callback. Sorry about that, that's kind-of important information.
You're going to want to use a callback method with your helper method.
So your helper defintion would look like this:
function dictionary(word, callback) {
request({}, function(err, res, body) {
if (err) {
return callback(err);
}
callback(null, body);
});
}
And your call would become:
dictionary('wut', function(err, result) {
if (err) {
return console.error('Something went wrong!');
}
console.log(result);
});
This is obviously a very simple implementation, but the concept is there. Your helpers/ modules/ whatever should be written to accept callback methods that you can then use to bubble up errors and handle them in the appropriate place in your application. This is pretty much the standard way to do things in Node.
Here is how you could call your helper using a simple Express route:
router.route('/:term')
.get(function(req, res) {
dictionary(req.params.term, function(err, result) {
if (err) {
return res.status(404).send('Something went wrong!');
}
res.send(result);
});
});
From my viewpoint, it looks like that request library you're using was designed asynchronous. This means that you should handle the data inside of the callback function.
For instance:
function handle(data) {
// Handle data
console.log(data)
}
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
handle(info.list[0].definition)
}
});
} else {
handle( dictionary[word] )
}
You didn't provide enough information for me to set this up correctly. But hopefully this gives you an idea on what you need to do.
To elaborate on why you should set it up this way:
The request function appears to be asynchronous, so keep it how it was designed.
You're returning inside of the callback, so your outer dictionary function isn't getting that returned data, the callback is.
Since the request function was designed async, there isn't a way to return the data to the dictionary without forcing it to be synchronous (which isn't advised). So you should restructure it to be handled inside of the callback.
(Another little note, you should use typeof dictionary[word] === "undefined", because I believe JavaScript sometimes throws an error otherwise.)