DEV Community

Dudi Nirgad
Dudi Nirgad

Posted on • Updated on

Express.js Async + Sync Global Exception Handler

Hi everyone, hope you all are doing ok in the COVID-19 world.
Recently I've been playing around with Express, and I needed to setup an error handler for my project.

I have been searching for a way to catch all exceptions in my project, something that will work both for sync and async routes.

To my surprise, I couldn't find an easy solution for it, so I had to come up with something of my own.

Let's walk it through:

first define your error handler
errorHandler.js:

const handleErrors = (err, req, res, next) => {
    if (res.headersSent) return next(err);

    // more handle error logic should be inserted here.

    res.status(500).json({
        msg: "Server Error",
    });

module.exports = handleErrors;

register your error handler (after all your routes)
server.js:

// Middlewares
app.use(cors());
app.use(express.json());
app.use(fileupload());
app.use(cookies());

// Routes
app.use("/", routes);
app.use(handleErrors); <------

Now we are going to do some javascript magic, we are going to leverage "express-async-handler" and write some of our own magic, to make the global exception handler work.

now we are going to hook express.Router which will return a new router that will have error handling for both async/sync routes

hookRouter.js

const express = require("express");
const originalRouterFunc = express.Router;
const asyncHandler = require("express-async-handler");

export function hookRouter() {
    function hookAllRouteMethods(router) {
        const methods = ["get", "post", "put", "delete", "options", "head", "all", "use"]; // all router methods
        methods.forEach(method => {
            const originalRouterFunc = router[method];
            const hookedRouterMethod = async function (path, ...routeHandlers) {
                routeHandlers = routeHandlers.map(f =>
                    asyncHandler(function (req, res, next) { // async error handler
                        // sync error handler start
                        try {
                            return f(req, res, next); // original route handler
                        } catch (e) {
                            console.log("hahah I caught you =)");
                            next(e, req, res); // pass exception to our error handler.
                        }
                        // sync error handler end
                    })
                );
                originalRouterFunc.apply(router, [path, ...routeHandlers]);
            };
            router[method] = hookedRouterMethod;
        });
    }

    function hookRouterCreation() {
        express.Router = function () {
            const router = originalRouterFunc.apply(this, arguments);
            hookAllRouteMethods(router);
            return router;
        };
    }
    hookRouterCreation();
}

next, we just have to hook our router at the top of server.js (or before you use routes):

const { hookRouter } = require("./hookRouter");
hookRouter();

That's it! now everytime you will use express.Router() it will have your error handler !

Hope this was helpful for you, Also this solution is still not 'battle tested' so I would like to hear what you guys think =).

Top comments (0)