DEV Community

Cover image for Catch ExpressJS Async Errors
Dak Washbrook
Dak Washbrook

Posted on • Edited on

Catch ExpressJS Async Errors

If you tried or happened to throw an error from within an async express route handler then you are probably looking at an error that looks like this:

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with.catch(). (rejection id: 1)

[DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Enter fullscreen mode Exit fullscreen mode

Trying to find the cleanest solution to this has led me to quite a few places that offer the following solutions:

  • Wrapping all request handler logic in try/catch blocks to pass the caught error to the next() function
  • Wrapping every request handler inside a function that checks if the handler is an async function to further wrap it with a .catch(next).

You can see both of these explained here: https://zellwk.com/blog/async-await-express/. These are both viable solutions. Though, I wanted to seek out a way to do this passively instead of actively.

This is what led me to utilize JavaScript's native Proxy built-in:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

Used in tandem with the handler.apply() method, explained here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply.

Below is a singular simple patch to fix this issue, without having to do anything with each async request handler.

const Layer = require('express/lib/router/layer')

Object.defineProperty(Layer.prototype, 'handle', {
  set: function (handle) {
    this._handle = new Proxy(handle, {
      apply: function (target, thisArg, argumentsList) {
        const type = target.constructor.name
        if (type === 'AsyncFunction') {
          const wrappedFunction = (...args) => {
            const next = args[args.length - 1] || Function.prototype
            target.apply(thisArg, args).catch(next)
          }
          wrappedFunction(...argumentsList)
          return
        }
        target.apply(thisArg, argumentsList)
        return
      }
    })
  },
  get: function () {
    return this._handle
  }
})
Enter fullscreen mode Exit fullscreen mode

Import or run this piece of code upon server startup and it will patch express.

There is a very similar version of this here https://github.com/davidbanham/express-async-errors/blob/master/index.js as well.

Let me know your thoughts in the comments below!

Top comments (0)