Error conditions for NPM Request lib - javascript

An error is being passed by the callback in my request function. I am trying to determine under what conditions an error is passed to the callback.
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if(error){
//why or when would an error be created?
}
else if (response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
else{
// when would this happen ?
}
})
the documentation doesn't seem to cover what conditions will cause an error object to be created and passed. Right now I just assume anything but a 200 or 300 will cause an error to be created, but I am just guessing.

request library uses the node.js http module internally for making GET request. From it's doc:
If any error is encountered during the request (be that with DNS
resolution, TCP level errors, or actual HTTP parse errors) an 'error'
event is emitted on the returned request object.
I guess you have to go though the http module source to exactly find out what are the errors.

Related

NodeJS Express Api -- calling res.send outside route works but res.status does not work no matter what

To keep things clean in my express route page I have a local function that is called in every route and it passes the sql query together with the req and res objects.
This works fine for sending a successful result and calling res.send works.
The problem that I'm having is I can't seem to find a way to get res.status to work and no matter the syntax it simply times-out and gives no error whatsoever in the console OR on the front end.
The tricky thing is, when it's inside the specific route it does work but the error message does not seem to get sent through instead it's just blank body?
`async function queryDatabase(queryParam, req, res) {
try {
const cp = new sql.ConnectionPool(config);
await cp.connect();
let result = await cp.request().query(queryParam);
cp.close();
res.send(result.recordset);
} catch (err) {
res.statusMessage = `Database error: ${err}`;
res.status(520);
}
}`
res.status(520) only sets the status value in the response object. It does not actually send the response. So, to send the response, you have several options. In the more recent versions of Express, you can use this shortcut:
res.sendStatus(520);
This will both set the status and send the response.
But, you can also do this in any version of Express:
res.status(520).end();
Which also sets the status and then sends the response.
You should end your response, use res.status(520).end() instead of res.status(520)

vue-resource: catch "Uncaught (in promise)" when intercepting an ajax error

I'm using vue-resource to fetch data from the server. A user needs to have a JWT token to get the correct data. If the token is invalid or expired, a 401 status is returned. If the user tries to access a forbidden page, a 403 is returned.
I would like to catch those errors and handle them appropriately (globally). This means, that the calls should be completely handled by the interceptor (if 401, 403).
How can I prevent the browser message "Uncaught (in promise)" and create some global error handling? I don't want to have a local error handler on every call.
I have the following interceptor:
Vue.http.interceptors.push(function (request, next) {
request.headers.set('Authorization', Auth.getAuthHeader());
next(function (response) {
if (response.status === 401 || response.status === 403) {
console.log('You are not logged in or do not have the rights to access this site.');
}
});
});
And the following call in the Vue methods:
methods: {
user: function () {
this.$http.get('http://localhost:8080/auth/user').then(function (response) {
console.log(response);
});
}
}
This is a bit of a dilemma, isn't it. You don't want unhandled promise rejections to get swallowed, because it means your application might not function and you won't know why, and will never get a report of the error happening.
On the other hand, it's silly to use the exact same error handling mechanism for every single .catch() statement in your application, so implementing a global error handler is definitely the way to go.
The problem is that in most cases, you will have to re-throw the error from your global error handler, because otherwise your application will think the request went through ok and will proceed to process the data, which will not exist.
But this leads to the situation where the Uncaught (in promise) error shows up, because the browser will think you didn't handle the error, whereas in reality you did, in your global error handler.
To get around this, there is now the onunhandledrejection event, and you can use that to prevent the browser from logging these errors, but then you have to make sure you process them yourself.
So what we often do is have our own error classes, and when a response error is thrown, we convert the error to one of our error classes, depending on the HTTP status code.
We also append a property to this error, something like ignoreUnhandledRejection and set it to true. Then, you can use the global handler to filter out those errors and ignore them, because you know that you have already handled them globally:
/**
* Prevent logging already processed unhandled rejection in console
*/
window.addEventListener('unhandledrejection', event => {
if (event.reason && event.reason.ignoreUnhandledRejection) {
event.preventDefault();
}
});

Error sending error to client side in nodeJs

I am using async in nodeJS, and in my final callback I am handling the error and trying to send it back to my angular controller.
function (err, data) {
if (err) {
console.log(err);
res.status(500).send({ err : err});
}
else {
res.json({data: data});
}
});
Now the error in the console is.
[Error: Username is already in use]
I am not able to get this particular error in my angular controller I tried sending the error in all combinations such as .
res.status(500).send({ err : err[0]});
res.status(500).send({ err : err.Error});
This is what I get in my front end.
Object {data: Object, status: 500, config: Object, statusText: "Internal Server Error"}
config
:
Object
data
:
Object
err
:
Object
__proto__
:
Object
__proto__
:
Object
headers
:
(d)
status
:
500
statusText
:
"Internal Server Error"
How can I bring that username in use error to my front End.
500 errors are usually reserved for Server errors, and not for scenarios like the one you have described. Server errors should be handled by your server and elegantly presented to your front end. Client errors should be in the 400s. Why don't you try a 409 or a 400:
res.status(409).json({error: "Username is already taken"});
Look at HTTP Status codes for more:
409 Conflict
The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.
Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.
Note: Also, as a good practice, make sure you return your res. functions, for predictable flow of control, like so:
return res.status(409).json({error: "Username is already taken"});

Need a way to systematically handle errors/exceptions/rejections in a NodeJS project

Background Info
I have a new project I'm working on that will provide multiple different (optional) packages that can be installed, all of which are in addition to the core package (only manual package). The other packages just interact with the core.
The project is just meant to keep track of lists of data (not very specific, I know, but these details aren't needed). The add-on packages determine HOW the lists of data are interacted with. The core package just consists of all the main JS functionality and database models, and authentication. The other packages tie into those.
Lets say you want to just have it as a standard web page, you can install the webui package, which will tie into the core, and create a web app for it
If you want to create an API, you can install the restapi package, which creates the RESTful interface; You can also install the spaui package which will interact with the RESTful interface, which gets the data from the core
These addon packages I will call "facade" packages. All you really need to extrapolate from the above is that the core is a separate package from the facade packages, and it handles the core functionality (Database stuff, authentication, authorization, etc)
Problem
The core can use promises or callbacks, and it returns exceptions for failures, then whatever facade package is used to interact with the core will handle the exceptions/errors (showing an HTTP error page, returning a RESTful error result, etc).
Since the package that handles the errors is different than the package that returns the errors, there needs to be a systematic way of knowing what type of error was returned, so it can be dealt with properly (EG: The webui/restui packages should know if it needs to show a HTTP 500, a HTTP 403, HTTP 409, etc). Obviously of the core just returns new Error('Something broke'), then the facade packages don't really know what type of error it is, unless they have the text saved somewhere and can match it up with an error code.
Question
Whats the best way to handle this? I haven't been able to find anything that accomplishes this exactly how I want..
I eventually started working on my own attempt.. (below)
My Possible Solution (If this is sufficient, just confirm)
I created a new AppError exception type, and instead of returning AppError exceptions with simple strings, you provide an error code which will associate that exception with the error message, error type, etc.
Here is an example usage of the AppError exception:
exports.createThing = ( name, data ) => {
return new Promise( ( res, rej ) => {
if( doesItExist( name ) )
return rej( new AppError( 'document.create.duplicateName' ) )
// Other stuff...
})
}
Now inside the AppError exception method, it takes the code and looks inside a list of exceptions (the code should be the key inside an object of exception data).
Heres an example of what the exception data object for the above exception would contain:
module.exports = {
'document.create.duplicateName': {
type: 'DocumentConflict',
message: 'Failed to create new document',
detail: 'The document name specified already exists, try another one'
}
}
Example Usage: Lets say we try to execute createThing with an already existing name (From within the webui package):
CorePackage.createThing( 'foobar', 'some data' )
.catch( err => {
/*
The err is now an instance of AppError
err.type -> DocumentConflict
err.message -> Failed to create new document
err.detail -> The document name specified already exists, try another one
*/
})
From here, it's as simple as associating the err.type value with a suitable HTTP error code! (which would probably be HTTP 409 Conflict). Obviously these associations can be kept in an object, making it easy to just retrieve the correct error code for any of the error type values returned. Then the text for the error code is right there in err.message and err.detail
This also makes it easy to introduce some type of locale into the application, as the error, as all that needs to be done is to edit the exception data object.
End of post
So if you think my solution above is a sufficient one, and you cant think of any problems, then please say so. Id like to know if it is or if it isn't. Even if you can't think of a proper solution, but you just know the one I created wont work, share that as well.
If you have an alternative solution, then that would work just as well!
Thanks
I think there are two basic ways to approach this:
code property: Create a new \Error object and assign the code property with information about the error. For example:
var err = new Error('Message');
err.code = "DocumentConflict";
Custom error objects. You could have a seperate Error object per error type that you have. For example, rather than having just AppError, you can have DocumentConflict error.
For projects where I am creating a RESTful API, I like to think in terms of error codes. For most projects, the endpoints will return one of the following codes:
400 (Bad Request)
401 (Credentials Error)
403 (Forbidden)
404 (Not Found).
500 (Internal Server Error).
These then become 'standard' types of Error that I pass around the application. A normal Error object is interpretated as an internal server error, so this will always pass 500 to the endpoint.
For example,
CredentialsError = function (message) {
Error.call(this, arguments);
Error.captureStackTrace(this, this.constructor);
this.message = message;
};
util.inherits(CredentialsError, Error);
CredentialsError.prototype.name = "CredentialsError";
And then just return/throw a new CredentialsError("Invalid password") object as necessary. To check the type of object, you can use instanceof. With Express, for example, you can have an error handler similar to the following:
app.use(function(err, req, res, next) {
var status;
if (err instanceof error.FieldError) {
status = 400;
} else if (err instanceof error.CredentialsError) {
status = 401;
/* etc */
} else {
status = 500;
}
if (status !== 500) {
res.status(status).send(JSON.stringify(
err,
null,
4
));
} else {
// for 500, do not output the error!
console.error(err.stack);
res.status(500).send({
message: "Internal Server Error"
});
}
});
It is also worth noting that you can defined your custom error object constructors to take more than just strings. For example, you can pass objects into a BadRequestError constructor to provide field-level error detail.
Now, in most cases, you can just propagate the errors and the response to the endpoint will make sense. However, there are cases where you want to transmute the type of error. For example, if you have a login endpoint, you might do a request to findUserByEmailAddress(). This could return a NotFoundError object, but you want to capture this in the signIn() function and transmute it to a CredentialsError.

How to handle ETIMEDOUT error?

How to handle etimedout error on this call ?
var remotePath = "myremoteurltocopy"
var localStream = fs.createWriteStream("myfil");;
var out = request({ uri: remotePath });
out.on('response', function (resp) {
if (resp.statusCode === 200) {
out.pipe(localStream);
localStream.on('close', function () {
copyconcurenceacces--;
console.log('aftercopy');
callback(null, localFile);
});
}
else
callback(new Error("No file found at given url."), null);
})
There are a way to wait for longer? or to request the remote file again?
What exactly can cause this error? Timeout only?
This is caused when your request response is not received in given time(by timeout request module option).
Basically to catch that error first, you need to register a handler on error, so the unhandled error won't be thrown anymore: out.on('error', function (err) { /* handle errors here */ }). Some more explanation here.
In the handler you can check if the error is ETIMEDOUT and apply your own logic: if (err.message.code === 'ETIMEDOUT') { /* apply logic */ }.
If you want to request for the file again, I suggest using node-retry or node-backoff modules. It makes things much simpler.
If you want to wait longer, you can set timeout option of request yourself. You can set it to 0 for no timeout.
We could look at error object for a property code that mentions the possible system error and in cases of ETIMEDOUT where a network call fails, act accordingly.
if (err.code === 'ETIMEDOUT') {
console.log('My dish error: ', util.inspect(err, { showHidden: true, depth: 2 }));
}
In case if you are using node js, then this could be the possible solution
const express = require("express");
const app = express();
const server = app.listen(8080);
server.keepAliveTimeout = 61 * 1000;
https://medium.com/hk01-tech/running-eks-in-production-for-2-years-the-kubernetes-journey-at-hk01-68130e603d76
Try switching internet networks and test again your code. I got this error and the only solution was switching to another internet.
Edit: I now know people besides me that have had this error and the solution was communicating with the ISP and ask them to chek the dns configuration because the http request were failing. So switching networks definitely could help with this.
That is why I will not delete the post. I could save people a few days of headaches (especially noobs like me).
Simply use a different network. Using a different network solved this issue for me within seconds.

Categories

Resources