Mark McDermid Mark McDermid - 5 months ago 8
Node.js Question

How can I wrap every every express route handler with another function

Basically I want to instead of this...

app.get(routes.test, function(req, res, next){
actualRouteHandler(req, res, next) // Always returns a promise or throws.
.catch(function(err) {
next(err);
});
});


Have this:
app.get(routes.test, catchWrap(actualRouteHandler));


Or something similar, I've tried messing around with fn.apply and things but I can't find a way to pass actualRouteHandler the correct parameters (req,res,next) and still have the function. Do I need to return a function or something similar?

Edit: I think there may be libraries that do this but we have no access to the actual express application in this bit of code.

Answer

In your specific case, catchWrap would look like this:

function catchWrap(originalFunction) {
    return function(req, res, next) {
        try {
            return originalFunction.call(this, req, res, next);
        } catch (e) {
            next(e);
        }
    };
}

That returns a new function that, when called, will call the original with your catch wrapper around it. The key parts are that it creates and returns a function (return function(req, res, next) { ... };) and this line:

return originalFunction.call(this, req, res, next);

Function#call calls the given function saying what to use as this during the call (in the above we pass on the this we recieved) and the arguments to use in the call.

You'd use it as you showed:

app.get(routes.test, catchWrap(actualRouteHandler));

Or if you prefer to define the actual handler as an anonymous function:

app.get(routes.test, catchWrap(function(req, res, next) {
    // ...handler code here...
}));

That catchWrap is specific to your situation, because you want to call next(e) with the exception if thrown. The generic form of "wrap this function in another function" is like this:

function catchWrap(originalFunction) {
    return function() {
        try {
            // You can do stuff here before calling the original...
            // Now we call the original:
            var retVal = originalFunction.apply(this, arguments);
            // You can do stuff here after calling the original...
            // And we're done
            return retVal;
        } catch (e) {
            // you can do something here if you like, then:
            throw e; // Or, of course, handle it
        }
    };
}

arguments is a pseudo-array provided by JavaScript that includes all of the arguments that the current function was called with. Function#apply is just like Function#call, except you give the arguments to use as an array (or pseudo-array) rather than discretely.