0

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?

myNewAccount
  • 578
  • 5
  • 17

1 Answers1

1

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.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • your link is gold and I've already read through it twice. There is one, small technical detail that I don't understand. In the line, `fn(req, res, next).catch(next);` why isn't it `catch(next( new Error(error)))` or something similar? It looks like I'm passing a reference to a method without any parameters. How does it know what to use? – myNewAccount Feb 03 '20 at 04:14
  • 1
    @myNewAccount - Because `.catch(next)` tells the promise that if it gets an error, it should call `next(err)` where `err` is the reject reason from that promise. It's the same as `.catch(err => next(err))`. That `err` will already be an `Error` object from the rejected promise - it doesn't need to be wrapped in one again. – jfriend00 Feb 03 '20 at 04:16
  • That is fascinating. Is there a super basic (even tiny) code pattern where I could test this concept in a different way? A line in which a parameter knows how to get into a function reference without the parameter being included? Just like the `.catch(next)` but something else? `myfunction(anotherFunction)` I think if I could test this in a different way my brain could understand this fundamental concept. – myNewAccount Feb 03 '20 at 04:28
  • 1
    @myNewAccount - `.catch()` expects you to pass it a function reference that expects one argument to be passed to it and it may call that function some time in the future with that one argument. `next` is exactly a function reference that expects to be called with zero or one argument. This is not unique to `.catch()`. It works this way for any function that wants you to pass it a callback. You can either define the callback inlne as in `.catch(function(err) { some logic here})` or you can pass a previously defined function as in `.catch(next)`. This is just plain Javascript. – jfriend00 Feb 03 '20 at 04:32
  • I think that's why my brain is melting. I'm not understanding a fundamental js concept. Here is a super, simple example. A number doubler inside of a wrapper. `function double(x){ return x * 2;} function wrapper(fun){ return fun();} wrapper(double);` Can this simple example be reorganized to show the basic JS concept you're referring to? I know I'm close but I'm missing something. – myNewAccount Feb 03 '20 at 04:54
  • 1
    @myNewAccount - As many examples as I can make here: https://jsfiddle.net/jfriend00/cv5L4go2/. Not sure what else to explain. – jfriend00 Feb 03 '20 at 05:10
  • Thanks Jfriend00! I thought something deeper was happening that I didn't understand. But your examples showed me it's the same Javascript that I already know and love. 8-) – myNewAccount Feb 03 '20 at 17:40