Skip to content

Instantly share code, notes, and snippets.

@lionello
Last active January 5, 2019 03:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lionello/05617e80f3ab9610ded404566e53c7db to your computer and use it in GitHub Desktop.
Save lionello/05617e80f3ab9610ded404566e53c7db to your computer and use it in GitHub Desktop.
Add support for async middleware and handlers to Express
/**
* Wrap an Express middleware or handler for async support.
* @param {function} fn The middleware or handler to wrap
* @returns {function} Wrapped function
*/
exports.wrap = function(fn) {
if (fn.length === 4) {
// Wrap error handler
return (err, req, res, next) => {
const r = fn(err, req, res, next)
if (typeof r === 'object' && typeof r.catch === 'function') {
r.catch(next)
}
}
} else if (fn.length === 3) {
// Wrap middleware; assumes middleware calls `next`
return (req, res, next) => {
const r = fn(req, res, next)
if (typeof r === 'object' && typeof r.catch === 'function') {
r.catch(next)
}
}
} else if (fn.length === 2) {
// Wrap a handler, calling `next` when it resolves
return (req, res, next) => {
const r = fn(req, res)
if (typeof r === 'object' && typeof r.catch === 'function') {
r.catch(next)
}
}
} else {
// Don't know what to do with this function; return as-is
return fn
}
}
/**
* Hook into Express' Layer class to automatically wrap each handler
*/
exports.autoWrap = function() {
const Layer = require('express/lib/router/layer')
// Don't hook if already hooked
if (!Layer.prototype.hasOwnProperty('handle')) {
Object.defineProperty(Layer.prototype, 'handle', {
get: function() {
return this.__handle
},
set: function(fn) {
this.__handle = exports.wrap(fn)
}
})
}
}
/**
* Decorate an Express App or Router with getAsync, postAsync, etc..
* @param {Express.App|Express.Router} appOrRouter Express App or Router
* @return {Express.App|Express.Router} same as appOrRouter
*/
exports.decorateApp = function(appOrRouter) {
for (let verb of ['post', 'get', 'all', 'patch', 'use', 'head', 'option', 'put', 'delete']) {
appOrRouter[verb + 'Async'] = function(route, ...handlers) {
// eslint-disable-next-line security/detect-object-injection
appOrRouter[verb].call(appOrRouter, route, ...handlers.map(exports.wrap))
}
}
return appOrRouter
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment