Best practices for handling uncaught Node exceptions with Express.js - node.js

I am writing a website with express and I found out that an uncaught exception would bring down the whole server(at least in debug mode).
Of course I could wrap every handler with (...).catch(err) and pass error with next(err), but that is just bad practice and wouldn't solve the problem (that the server goes down on uncaught error).
Before express I only had experience with Laravel and Laravel handles uncaught errors gracefully by sending a 500 to the user, unlike express.
Any thoughts? Perhaps this is easily googleable and I am being dumb asking this, but so far the best approach I found is catch every error manually (which is bad practice IMO, as it leaves possibility of server going down on uncaught exception)

If you switch to promises, you can write a single wrapper function that catches every error and calls next(err) for you, or use this package:
https://www.npmjs.com/package/express-async-handler
Otherwise, you should catch every asynchronous error.
Express is old and was written in a time before promises. There's some nicer frameworks out there these days that have better default behavior.

Express already catches synchronous exceptions for you and already calls next(err). All you need is an Express error handler defined.
Here's the simplest example:
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
});
Express will not catch asynchronous exceptions or promise rejections for you automatically. To catch promise-rejections, you will have to do some sort of wrapper for all route handlers. There are a variety of ways to implement this wrapper. I've written a bit of a patch for Express that I sometimes use in my apps that automatically handles promise rejections in route handlers if they return a promise or if they are async and an await sees a rejected promise. It's unfortunate that Express is not being updated to handle this type of thing.
Asynchronous (callback-based) errors must be handled inside the callback since there's no universal way to propagate that error anywhere (this is why promise rejections are a better way to code).

Related

How do you handle errors thrown from third-party middleware in express?

let's take this snippet for example
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.get(...)
# all my routes #
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
app.listen(9000)
....
if body-parser throws an error?
Maybe the client closes the connection or the request payload is too large
the client will receive an error with status code 500 from the default express error handler but it’s probably a error that should return 4xx response .
I thought of putting a middleware right after it and calling res.send with the error and status code 400.
maybe wrap all my 3rd party middle ware with try-catch handler
Or adding in my error handler middleware a huge switch case
And over time adding error thrown from middleware third party middleware and change there status code to 400
All the solutions not really bolt proof and need maintenance over time
What us the best practice any suggestions?
The body-parser module should call next(err) when it encounters an error and you can then intercept that by providing an Express error handler such as:
app.use(function(err, req, res, next) {
console.error(err.stack)
res.status(500).send('Put your content here')
});
When you define this special middleware (with four arguments), then Express will keep track of that separately as an error handler that should be called when/if some request handler calls next(err) or if a synchronous exception is caught by Express.
Express has a default error handler if you do not provide one which you can read about here. But, if you supply one, then yours will take over and you can do whatever you want from the error handler. You can even just log the error and take the user to an error page - you don't have to even send an http error status if it's a web page request. If it's an API, you should probably be using http error statuses.
what is the best practice?
Best practice is to include your own Express error handler where you will be able to intercept all errors that are sent on to next(err).
I thought of putting a middleware right after it and calling res.send with the error and status code 400 or maybe wrap all my 3rd party middle ware with try-catch handler
Your third party middleware needs to behave properly in the Express eco-system. That means that it can throw synchronously and Express will catch it automatically and send to the Express error handler. The code should not throw asynchronously (as is true for all types of code) because nobody can catch those exceptions (unless they are in promise-structured code). You shouldn't have to wrap middleware yourself.
Internal errors should generally not be 4xx errors, but rather 5xx errors. Whether or not you surface an internal error back to the end user depends entirely upon the context. Usually, the user will be shown some sort of general purpose error page. Or, if it's an API, the API will have a scheme for communicating errors and typically the API will return actual http status codes that appropriately map to the type of problem.
after further investigation, this is the best solution suited for me
simply adding another middleware after body parse
https://www.npmjs.com/package/express-body-parser-error-handler

Custom Error handling in Express submodules (e.g. in Service Layer)

I know about the Express documentation regarding Error handling, namely using next() to handle errors in async functions or throw to handle errors in sync functions. Throwing those from my route handler works fine, errors get passed to my custom errorHandler that I inserted via app.use().
However, what I never found in any documentation is, how would I best handle errors in an async function (e.g. in my Service Layer) or class that is called by a Route handler. Do I pass next() to that function? I really don't want to do that, as I want to keep all Express specific functions separate from my other application logic.
class someClassThatIsUsedInMyRouteHandler {
async create() {
throw new CustomError("some message", 401);
}
}
Oh man, tried a few things and in the end the problem was a type (error instead of err in my errorHandler). Doing next(error) in my controller works :)

Node.js & Express - Are Error objects ever created and passed to middleware without first being declared inside route?

Environment: Node.js, Express
The pattern below will forward all synchronous and asynchronous Error objects to my error handling middleware.
app.get('/', wrapAsync(async function(req, res, next) {
// synchronous error
throw new Error('oops');
// asynchronous error
//next( new Error('oops I did it again');
res.send('hello world');
}));
function wrapAsync(fn) {
return function(req, res, next) {
fn(req, res, next).catch(next);
};
}
app.use( function(error, req, res, next) {
res.send(error);
});
However what if an unexpected error occurs in part of my code where I didn't set up an Error object? Will node.js or Express detect that an error occurred in my route, create an Error object and forward it to my middleware through the wrapAsync wrapper function? This is hard for me to wrap my mind around because I'm not sure how to test for something unexpected.
Is there a pattern that ensures that all possible errors that occur in a route are forwarded to the error handling middleware without crashing the server?
However what if an unexpected error occurs in part of my code where I didn't set up an Error object?
If something throws besides your own code (a programming error or unexpected exception), than whatever threw will have created its own exception. It is convention, but not entirely required that you throw Error objects. Custom code could throw a string or its own object if it wanted to, though that is not the common convention.
An interpreter-generated exception (such as a TypeError) will always throw some type of Error object.
Will node.js or Express detect that an error occurred in my route, create an Error object and forward it to my middleware through the wrapAsync wrapper function?
That's not really the right way to think of it. It's not Express or node.js doing it. It's whatever code caused or threw the exception in the first place (either manually throwing an exception or the interpreter ran into a error that leads to an exception. That's an exception and where they come from. Because you have wrapped things in an async function, you are likely to see that exception (and it's associated Error object) in your .catch() handler.
There are however situations where you still won't see the exception. If some asynchronous code inside your wrapper uses plain callbacks (not promises) and throws an exception inside that plain asynchronous callback, then your wrapper won't catch that exception (nothing will). That's why all asynchronous code in this architecture should be using promisified asynchronous functions only because it enables the automatic error propagation that you are relying on.
Is there a pattern that ensures that all possible errors that occur in a route are forwarded to the error handling middleware without crashing the server?
No. Not if a function uses plain, non-promisified asynchronous callbacks. As described above, in that circumstance the errors will not propagate up to your wrapper.
FYI, see Express middleware cannot trap errors thrown by async/await, but why? for a scheme for building in rejected promise detection into Express. There's also the Express cousin, koa that does this more automatically as part of its architecture.

Testing Express errors with Mocha and Supertest

I have to test for server errors (Express) in acceptance tests that can't (or shouldn't) be sent with response, for example
Error: Can't set headers after they are sent.
Catching an error with error handler and responding with 5XX code would provide valuable feedback here, but the problem is that the headers have been sent already.
This kind of bugs may be noncritical and hard to spot, and usually they are figured out from the logs.
The spec is
it('should send 200', function (done) {
request(app).get('/').expect(200, done);
});
And tested app is
app.get('/', function (req, res, next) {
res.sendStatus(200);
next();
});
app.use(function (req, res) {
res.sendStatus(200);
});
What is the most appropriate way to communicate between Express app instance and request testing library (i.e. Supertest) in similar cases?
The question is not restricted to Supertest. If there are packages that can solve the problem that Supertest can't, they may be considered as well.
Try to just set the status code without send it and avoid send it twice throwing the error use res.status().
As the express documentation say it
Sets the HTTP status for the response. It is a chainable alias of
Node’s response.statusCode.
IMHO if you want to detect it in a end-to-end (E2E) testing tool like supertest (or Selenium) you have to handle the express error and send a correct output (500 status error, some message...) to permit detect it.
Or use instead a unit test, to test that the controller function doesn't throw any error using chai or a native assertion.
I answered a similar question here. The "can't set headers after they have been set" error should be raised by express as an unhandled exception. So, you should be able to get access to it via the unhandledException event that is raised by the process.
However, this is much more tricky because of the timing. Your test case's expect function and done function will be queued for processing on the event loop on the tick right after the first res.statusCode call. Unfortunately, the next call for res.statusCode can happen at an indeterminate amount of time afterwards. For example, what if the second route handler called a really slow webservice or db and then called res.statusCode.
With that in mind, your options are pretty hard. The brute force way is to wait in your test code for a determinate amount of time and then check. It's effective but slow and non-deterministic, which will cause your test to be flaky.
Another option is to check any instrumentation code that you might have in express. If you have code in express that keeps metrics of the number of in process calls for the various route handlers you could expose these metrics to your test code. Then one of your conditions for finishing your test is that all metrics for in process route calls are 0. The second option would allow you to write deterministic tests and be much faster because you could poll the metrics.
The final option would be to handle this test case through unit tests. This is probably the best solution because it would be deterministic and wouldn't require any sort of polling. However, the downside is that you need to know that both of your functions are called in order which leads you down a path of trying to recreate in your test code the logic that express uses to call route handlers.
I did this using HAPI instead of Express, but I solved the same problem. I used an external library to make the call (like request-promise) and it worked. Catch the error in the request-promise response.

Handling errors in an express middleware module

I'm creating a module, which is an express middleware. My question is, when it encounters some kind of error (for example an acl module that sees that the user has no access right to this content), what is the best practice, calling next() with an error, so the app using the module can handle it the way it chooses, or can I send a response and not even call next?
Yes, you pass a error into next function to allow user catch error in callback and do what he needs.
More you can read in this articles:
Who provides the next() function in Express middleware?
https://blog.safaribooksonline.com/2014/03/10/express-js-middleware-demystified/

Resources