How to handle developer errors in Express.js? - node.js

I have to differentiate between operational and programmer(developer) errors. All errors with stack property are marked as developer errors. I log them and crash the server.
My question is if this approach will correctly identify all programmers errors (tried to read property of "undefined", called an asynchronous function without a callback, passed a "string" where an object or int was expected etc.)
Here is my error handler:
app.use((err, req, res, next) => {
// Delegate to the default Express error handler,
// when the headers have already been sent to the client
if (res.headersSent) return next(err);
// Decorate with additional properties from Boom
// If err has no specified status code or there is a stack, it is most likely a programmer error.
// Server must crash and restart. Default error code is set to 'Internal Server Error (500)'
const options = err.stack || !err.statusCode
? { data: { stack: err.stack || 'n/a', developerError: true } }
: {};
if (!err.isBoom) boom.boomify(err, options);
// Add more details
const message = {
...err,
originalUrl: req.originalUrl,
method: req.method,
ip: req.ip
};
// Log server errors only. No need to log 402, 403 etc.
if (err.isServer) winston.error(message);
// Crash server in case of a developer error.
// NOTE: a Node.js process manager should be set up to immediately restart the crashed server
// eslint-disable-next-line no-process-exit
if (err.data && err.data.developerError) process.exit(1);
return res.status(err.output.statusCode).json(message);
});

Related

How to auto send error of try catch block to Sentry in Node JS

I am building a Node JS application. I am using Sentry, https://docs.sentry.io/platforms/node/ in my application to monitor and report errors. But I am having a problem with global reporting for try catch block.
For example, I have a code block as follow.
const getUser = async (id) => {
try {
//do the database operation and return user
} catch (e) {
return {
data: null,
message: e.message
}
}
}
As you can see in the code, I am catching the error in the Try catch block. If I want to report the error to Sentry, I have to put in the following line in the catch block.
Sentry.captureException(e);
Basically, I am explicitly reporting the error. Is there a way to globally and automatically catch the error within the catch block and report it to the sentry. For, example, something like in PHP or Laravel. We will just have to initialize and configure the Sentry in one centralized place of the application and app will report any errors to the Sentry.
Sentry starts monitoring the whole application just by adding, the init function, somewhere in a global scope.
For example :
Sentry.init({
debug: appConfig.env === 'staging',
dsn: appConfig.sentryDSN,
environment: appConfig.env,
integrations: [new Integrations.BrowserTracing()],
release: [pjson.name, pjson.version].join('#'),
tracesSampleRate: 1.0,
ignoreErrors: [],
normalizeDepth: 10, // Or however deep you want your state context to be.
// (breadcrumb: Breadcrumb, hint?: BreadcrumbHint | undefined) => Breadcrumb | null
beforeBreadcrumb(breadcrumb: Sentry.Breadcrumb, hint: Sentry.BreadcrumbHint | undefined) {
return breadcrumb.category === 'xhr' ? breadcrumb : null;
},
});
You can stay just to that 'conf/init' if you like, this captures some errors, which are: `every error that inherits from the 'Error' object, like: TypeError, RangeError, ReferenceError, SyntaxError, etc , for more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
But its better to handle the errors explicitly, and have the power to control what you want to send to Sentry(add/filter breadcrumbs, add tags, extra data etc).
In my reactjs app i have a middleware that all the errors are sent there, and inside there there is a logic..
Similarly i d suggest an error middleware where all the errors are send there, and at that middleware you exlicitly handle & send them to Sentry.
I assume the the tech stack node + express, so i d suggest, in the routes catch, to call next(error):
router.get('/path', function(req, res, next){
const getUser = async (id) => {
try {
//do the database operation and return user
} catch (error) {
//return {
// data: null,
// message: e.message
// }
next(error)
}
}
})
**OR based on Express 5, route handlers that return Promise will call next(value) automatically when they reject or throw an error **:
app.get('/path', async function (req, res, next) {
var user = await getUser(id)
res.send(user)
})
And into the app.js, you put the error handler middleware where it handles the errors..:
app.use(function(err, req, res, next){
// filter the error
// send it to Sentry
if(condition){
// add custom breadcrumb
Sentry.addBreadcrumb({
type: Sentry.Severity.Error,
category,
message,
level: Sentry.Severity.Error,
});
Sentry.configureScope((scope: any) => {
scope.setTag('section', section);// add tag
Sentry.captureException(error, scope); // capture the error
});
}
})

Access request/response object outside app.use for unhandledRejection event handler

So far, I have checked that to handle error for promise in generic way, unhandledRejection event handler is the best way to do it. Just like below:
process.on('unhandledRejection', (reason,promise) => {
console.log('error: unhandledRejection');
});
Now the challenge comes when I want to access Request and Response Objects inside this event handler. So that I can generate the 500 Response in generic way and hence I wont need to add promise exception handling everywhere in the project.
I have been suggested to use:
app.use(function (req, res, next) {
process.on('unhandledRejection', function (reason,promise) {
console.error("Here I can access request objects.")
// Unregister unhandledRejection
});
});
But in above case the event listener will be registered multiple times and will only be unregistered whenever exception occurred. As the event will be registered multiple times, So the code(console.error("Here I can access request objects.")) will be triggered multiple times.
Any suggestions ? If I can access request and response objects out side the app.use?
Thanks in Advance!
By default express does handle the error. By express documentation:
Express comes with an embedded error handler that will take care of any errors that may occur in the application. This default error-handling middleware function is added at the end of the middleware function stack.
next() If you give an error to the function and do not process it in a custom error handler, this error is handled by the embedded error handler; the error is printed on the client with stack-trace. Stack-trace is not included in the production environment.
Set the NODE_ENV value of the environment variable to production to run the application in production mode .
When an error is printed, the following information is added to the response:
res.statusCodethe value of the err.statusfield comes from the field
(or err.statusCode). If this value is not in the range of 4xx or 5xx,
it will be set to 500.
res.statusMessage field is set according to
the status code.
If in production mode, the body (body) becomes the HTML of the
status code message, otherwise err.stack.
err.headers Any header specified in object.
next() If you call the function with an error after starting to write the response (for example, if you encounter an error while transmitting the response to the client) the default Express error handler will close the connection and fail the request.
When you add a custom error handler, you should authorize the default Express error handler if the headers have already been sent to the client:
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}
Calling the next()function multiple times with an error in your code can trigger the default error handler, even if the custom error handler middleware is in place.
This is prone to memory leak:
app.use(function (req, res, next) {
process.on('unhandledRejection',
function (reason,promise) {
console.error("Here I can access request objects.")
// Unregister unhandledRejection
});
});
Also, you can't access the response object in 'unhandledRejection', and why do you want to access that if express can handle that for you to send '500' status code to the user. It is always a good practice to catch promises whenever and wherever required. However, if some promises are still not covered there are libraries to handle it too like : express-promise-router, express-async-errors
We could emit meaningful error response with err argument in express error handler and the response will be sent to the user. for example,
app.use((err, req, res, next) => {
if (err.name === 'CustomError' && err.message === 'EmptyResponse') {
return res.status(404).send('Not Found');
}
// ... more error cases...
return res.status(500).send('Internal Server Error');
});

How to handle callback error in node.js

I am new to node.js, I have a requirement where i am trying to handle the error that is being returned from the callback method/function. How do i assign the error that is being sent as part of callback to my response payload.
The node module that i am calling to validate swagger supports both callback function as well as Promise.
So how do i assign the err to my response payload. Currently i am just logging to my console, but since we plan to expose this through an API i would like to return the error information in the response payload.
var express = require('express');
var SwaggerParser = require('swagger-parser');
var myParser = require("body-parser");
var app = express();
var fs = require("fs");
app.use(myParser.urlencoded({extended : true}));
app.use(myParser.json());
function errorHandler (err, req, res, next) {
res.status(500)
res.render('error', { error: err })
}
app.post('/v1/swagger/validate',function(request,response){
/**SwaggerParser.validate(request.body, function(err, api) {
if (err) {
console.error(err);
console.log("Inside Error");
}
else {
console.log("API name: %s, Version: %s", api.info.title, api.info.version);
console.log("Inside Success");
}
}); **/
SwaggerParser.validate(request.body)
.then(function(api) {
console.log("API name: %s, Version: %s", api.info.title, api.info.version);
})
.catch(function(err) {
console.error(err);
});
response.end();
});
app.listen(8082);
You can decide how you want to communicate the error back from your API.
If the error is internal to your server and not something caused directly by a poor API request, then you probably return a 500 status code (internal server error).
response.status(500).end();
If there's something meaningful to communicate back to the other end of the API (like nothing found for the query or a specific validation error), then you have to design what you want that to be. For example, you could be sending back some JSON:
response.json({result: null, msg: "Validation Failed"});
So, it's really up to you what you want your API to return for a given situation. The main point is that you decide what you want that response to be and you send it as the response, even in error conditions. You need to make it a design that makes sense to the consumers of your API so they can clearly tell when they have a proper result and clearly tell when they have an error and if the error is their fault they need to be able to tell why it is their fault based on the response (so they more detail you provide on the issue in the response, the better).

Nodejs Using domain.dispose() causes server to respond with status 0

When I use domain.dispose() in Node.js expressjs based HTTP Server, the client making HTTP sees a response code of 0 (Could not get any response). If I remove domain.dispose() I receive 500 error with the desired error message. Below is my code
//Enable domains
app.use(function(req, res, next){
var createDomain = require('domain').create;
var domain = createDomain();
domain.add(req);
domain.add(res);
domain.run(function() {
next();
});
domain.on('error', function(e) {
//no further domain watch required
domain.dispose(); //if I remove this line status code of 500 is received on client, otherwise 0 or no response is received
next(e);
});
});
//Respond with 500 for Unhandled errors
app.use(function(err, req, res, next) {
// If the error object doesn't exists
if (!err) return next();
// Log it
req.log.error(err);
//console.error(err.stack);
try{
if(req.path && req.path.indexOf('/api/') === 0){
if(!res.headersSent){
console.log('responded with an error');
res.status(500).send({error: err.message});
console.log('responded with an error ACK');
}
return;
}
// Error page
res.status(500).render('500', {
error: err.stack
});
} catch(ex){
console.log('An error occured while responding 500');
req.log.error(ex);
}
});
Can anyone explain this or a better solution? Removing domain.dispose() may cause further exceptions, which may cause to re-enter the domain, and I do want to acknowledge client with the exception message as in my code.
This is expected behaviour of domains. Since you have explicitly added req and res to domain, so they are disposed as well. Don't use dispose, it does unexpected things. When you catch an error with domain the only sensible thing to do is to shutdown the process as quickly as possible.

How to send a custom http status message in node / express?

My node.js app is modeled like the express/examples/mvc app.
In a controller action I want to spit out a HTTP 400 status with a custom http message.
By default the http status message is "Bad Request":
HTTP/1.1 400 Bad Request
But I want to send
HTTP/1.1 400 Current password does not match
I tried various ways but none of them set the http status message to my custom message.
My current solution controller function looks like that:
exports.check = function( req, res) {
if( req.param( 'val')!=='testme') {
res.writeHead( 400, 'Current password does not match', {'content-type' : 'text/plain'});
res.end( 'Current value does not match');
return;
}
// ...
}
Everything works fine but ... it seems not the the right way to do it.
Is there any better way to set the http status message using express ?
None of the existing answers accomplish what the OP originally asked for, which is to override the default Reason-Phrase (the text appearing immediately after the status code) sent by Express.
What you want is res.statusMessage. This is not part of Express, it's a property of the underlying http.Response object in Node.js 0.11+.
You can use it like this (tested in Express 4.x):
function(req, res) {
res.statusMessage = "Current password does not match";
res.status(400).end();
}
Then use curl to verify that it works:
$ curl -i -s http://localhost:3100/
HTTP/1.1 400 Current password does not match
X-Powered-By: Express
Date: Fri, 08 Apr 2016 19:04:35 GMT
Connection: keep-alive
Content-Length: 0
You can check this res.send(400, 'Current password does not match')
Look express 3.x docs for details
UPDATE for Expressjs 4.x
Use this way (look express 4.x docs):
res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');
You can use it like this
return res.status(400).json({'error':'User already exists.'});
One elegant way to handle custom errors like this in express is:
function errorHandler(err, req, res, next) {
var code = err.code;
var message = err.message;
res.writeHead(code, message, {'content-type' : 'text/plain'});
res.end(message);
}
(you can also use express' built-in express.errorHandler for this)
Then in your middleware, before your routes:
app.use(errorHandler);
Then where you want to create the error 'Current password does not match':
function checkPassword(req, res, next) {
// check password, fails:
var err = new Error('Current password does not match');
err.code = 400;
// forward control on to the next registered error handler:
return next(err);
}
At server side(Express middleware):
if(err) return res.status(500).end('User already exists.');
Handle at Client side
Angular:-
$http().....
.error(function(data, status) {
console.error('Repos error', status, data);//"Repos error" 500 "User already exists."
});
jQuery:-
$.ajax({
type: "post",
url: url,
success: function (data, text) {
},
error: function (request, status, error) {
alert(request.responseText);
}
});
When using Axios you can retrieve the custom response message with:
Axios.get(“your_url”)
.then(data => {
... do something
}.catch( err => {
console.log(err.response.data) // you want this
})
...after setting it in Express as:
res.status(400).send(“your custom message”)
My use-case is sending a custom JSON error message, since I'm using express to power my REST API. I think this is a fairly common scenario, so will focus on that in my answer.
Short Version:
Express Error Handling
Define error-handling middleware like other middleware, except with
four arguments instead of three, specifically with the signature (err,
req, res, next). ... You define error-handling middleware last, after
other app.use() and routes calls
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Raise errors from any point in the code by doing:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Long Version
The canonical way of throwing errors is:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
By default, Express handles this by neatly packaging it as a HTTP Response with code 404, and body consisting of the message string appended with a stack trace.
This doesn't work for me when I'm using Express as a REST server, for example. I'll want the error to be sent back as JSON, not as HTML. I'll also definitely not want my stack trace moving out to my client.
I can send JSON as a response using req.json(), eg. something like req.json({ status: 404, message: 'Uh oh! Can't find something'}). Optionally, I can set the status code using req.status(). Combining the two:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
This works like a charm. That said, I find it quite unwieldy to type every time I have an error, and the code is no longer self-documenting like our next(err) was. It looks far too similar to how a normal (i.e, valid) response JSON is sent. Further, any errors thrown by the canonical approach still result in HTML output.
This is where Express' error handling middleware comes in. As part of my routes, I define:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
I also subclass Error into a custom JSONError class:
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Now, when I want to throw an Error in the code, I do:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Going back to the custom error handling middleware, I modify it to:
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
Subclassing Error into JSONError is important, as I suspect Express does an instanceof Error check on the first parameter passed to a next() to determine if a normal handler or an error handler must be invoked. I can remove the instanceof JSONError check and make minor modifications to ensure unexpected errors (such as a crash) also return a JSON response.
If your goal is just to reduce it to a single/simple line, you could rely on defaults a bit...
return res.end(res.writeHead(400, 'Current password does not match'));
Well in the case of Restify we should use sendRaw() method
Syntax is:
res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)

Resources