Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@thlorenz
Last active August 26, 2018 15:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thlorenz/265598d731e223e89e5c333f2d6662df to your computer and use it in GitHub Desktop.
Save thlorenz/265598d731e223e89e5c333f2d6662df to your computer and use it in GitHub Desktop.
async/wrap express wrapper

Summary

Tried to improve debugging when an express middleware is wrapped to auto-handle errors of an asnync function.

Turns out the below reads a bit better than a return Promise.catch() implementation, but still, once we reach the central express error handler, the line of the wrapped function that caused the error isn't included.

Implementation

'use strict'

function asyncify(fn) {
  async function asyncifyWrap(req, res, next) {
    try {
      return await fn.apply(null, arguments)
    } catch (err) {
      next(err)
    }
  }
  return asyncifyWrap
}

module.exports = asyncify

Sample Use

'use strict'

const express = require('express')
const helmet = require('helmet')

const { transports, Logger } = require('winston')
const morgan = require('morgan')

const request = require('request-promise-native')
const asyncify = require('./asyncify')

//
// Logging Setup
//
const consoleTransport = new transports.Console({
    level            : 'debug'
  , handleExceptions : false
  , colorize         : true
  , timestamp        : true
  , prettyPrint      : true
})

function devLogger() {
  const transports = [ consoleTransport ]
  return new Logger({ transports, exitOnError: true })
}

function morganMiddleware(logger) {
  return function morganHandler(tokens, req, res) {
    const { method, url, status } = tokens
    logger.info(
      `${method(req, res)} ${url(req, res)} ${status(req, res)} - ` +
      `${tokens['response-time'](req, res)} ms`
    )
    return null
  }
}

const log = devLogger()

//
// App Setup
//
const PORT = process.env.PORT || 4444
const app = express()
app
  .use(helmet())
  .use(morgan(morganMiddleware(log)))
  .get('/cause-error-wrap', asyncify(async function causeError(req, res) {
    // Super important to name our async function here as otherwise debugging becomes
    // very hard
    const result = await request(`h://www.google.com/search?q=node`)
    res.status(200).send('Surprisingly all good ' + result)
  }))
  .get('/cause-error', async function causeError(req, res, next) {
    // Manually wrapping async/await constructs inside try/catch as this improves debugging
    // compared to using wrapper functions greatly.
    try {
      const result = await request(`h://www.google.com/search?q=node`)
      res.status(200).send('Surprisingly all good ' + result)
    } catch (err) {
      next(err)
    }
  })
  .use((err, req, res, next) => {
    log.error(err.toString())
    log.debug(err.stack)
    res.status(500).send(err.message)
  })
  .listen(PORT)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment