Tower Complete by teotwawki |
A while ago I figured that implementing a middleware scheme would clean up the API of a library of mine, rest-sugar. It is a library that greatly simplifies the creation of RESTful APIs. And I have been polishing it and related technologies lately. Express-style middleware API seemed like a good fit so I implemented that. There are both pre and post filters in place now and I find the interface quite nice.
The following snippet illustrates how the pattern works. Note that it is possible to simplify it further in case you need only one type of filters. In that case you do not need to keep track of context like here.
function init() { var handlers = { pre: [], post: [] }; var context; // ... your logic goes here return { use: function(fn) { handlers[context].push(fn); }, pre: function(fn) { context = 'pre'; fn(); }, post: function(fn) { context = 'post'; fn(); } }; } // evaluate the middleware at your logic // Using https://github.com/caolan/async here. function evaluateHandlers(handlers, req, res, done, data) { async.series(handlers.map(function(fn) { return function(cb) { fn(req, res, cb, data); }; }), done); } // sample middleware function only(method) { return function(req, res, next) { if(req.method == method) return next(); unauthorized(res); }; } exports.only = only; // and usage var app = express(); ... var api = init(app); api.pre(function() { api.use(only('GET')); }) api.post(function() { api.use(function(req, res, next, data) { // trigger now // and remember to call next to avoid getting stuck! next(); }); });Even though the idea is quite simple I like how it gives me a nice way to define extension points to my APIs. You can also define factories to construct middlewares you need. In this case only is such a factory. That is another quite powerful and handy pattern enabled by closures.