I have Express.js instance and couple of routes which I want to wrap in some function. Example:
const wrapper = (route) => {
return (req, res, next) => {
let result = route(req, res, next);
// do some independent processing
}
};
app.get('/', wrapper((req, res, next) => {
// respond to request somehow
}));
While this works fine, I don't like the idea to explicitly call wrapper
on every route or middleware which requires such processing.
Is there any way to be able to wrap every required route/middleware in certain wrapper (given that wrapper
function can check that this route/middleware needs to be wrapped) implicitly (via Express.js
extension, monkey-patching or some special middleware)?
UPDATE:
More solid example. Let's assume I want to make an async
router functions. But I don't want to catch errors in each and every route function. So I wrap them up:
const wrapper = func => (req, res, next) => {
const promise = func(req, res, next);
if (promise.catch) {
promise.catch(err => next(err));
}
next();
};
app.get('/one', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/two', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/three', wrapper(async (req, res, next) => {
// respond to request somehow
}));
// and so on...
app.use((err, req, res, next) => {
// do something with intercepted error
});
This explicit wrapper
around all routes is actually the thing I want to get rid of.
I have Express.js instance and couple of routes which I want to wrap in some function. Example:
const wrapper = (route) => {
return (req, res, next) => {
let result = route(req, res, next);
// do some independent processing
}
};
app.get('/', wrapper((req, res, next) => {
// respond to request somehow
}));
While this works fine, I don't like the idea to explicitly call wrapper
on every route or middleware which requires such processing.
Is there any way to be able to wrap every required route/middleware in certain wrapper (given that wrapper
function can check that this route/middleware needs to be wrapped) implicitly (via Express.js
extension, monkey-patching or some special middleware)?
UPDATE:
More solid example. Let's assume I want to make an async
router functions. But I don't want to catch errors in each and every route function. So I wrap them up:
const wrapper = func => (req, res, next) => {
const promise = func(req, res, next);
if (promise.catch) {
promise.catch(err => next(err));
}
next();
};
app.get('/one', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/two', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/three', wrapper(async (req, res, next) => {
// respond to request somehow
}));
// and so on...
app.use((err, req, res, next) => {
// do something with intercepted error
});
This explicit wrapper
around all routes is actually the thing I want to get rid of.
- Is your ultimate goal to run some code when the response was sent? – robertklep Commented Jun 2, 2017 at 11:27
- No, my goal is decorate the router function so I can get direct response result (for example, return Promise) and do something with it. – Damaged Organic Commented Jun 2, 2017 at 12:40
3 Answers
Reset to default 5It turned out to be a bit of a PITA because, ultimately, Express doesn't propagate the return value of a route handler function.
This is what I came up with (a monkey-patch):
const Layer = require('express/lib/router/layer');
const handle_request = Layer.prototype.handle_request;
Layer.prototype.handle_request = function(req, res, next) {
if (! this.isWrapped && this.method) {
let handle = this.handle;
this.handle = function(req, res, next) { // this is basically your wrapper
let result = handle.apply(this, arguments);
// do some independent processing
return result;
};
this.isWrapped = true;
}
return handle_request.apply(this, arguments);
};
I would probably suggest using a similar approach as express-promise-router
though, which implements a drop-in replacement for Express' Router
. However, it's not implicit.
Why not just use next()?
You can add stuff on req like
app.get('/', (req, res, next) => {
req.somestupidfieldthatidontevenknowwhyinamedthisway = 42;
next();
});
app.get('/', (req, res, next) => {
//req.somestupidfieldthatidontevenknowwhyinamedthisway is now accessible as 42
var valueFromPreviousMiddleware = req.somestupidfieldthatidontevenknowwhyinamedthisway;
.....
});
You could wrap middleware and router as below
function wrapper(func) {
return function inner(req, res, next) {
const start = Date.now();
func(req, res, function () {
let elapsedMS = Date.now() - start
console.log('time elapsed for function ' + func.prototype.constructor.name + ' is ' + elapsedMS)
next.apply(this, arguments);
});
};
}
var originalAppUse = app.use;
app.use = function () {
lastArg = arguments.length - 1;
if (typeof arguments[lastArg] === 'function') {
arguments[lastArg] = wrapper(arguments[lastArg])
}
originalAppUse.apply(this, arguments)
}