DEV Community

Cover image for Catch ExpressJS Async Errors
Dak Washbrook
Dak Washbrook

Posted on • Edited on

1

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!

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay