Last active
January 5, 2019 03:56
-
-
Save lionello/05617e80f3ab9610ded404566e53c7db to your computer and use it in GitHub Desktop.
Add support for async middleware and handlers to Express
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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