Express: Consider support for dynamic middleware loading

Created on 27 Sep 2013  路  24Comments  路  Source: expressjs/express

I've been playing around with a server that can dynamically configure itself with middleware based on user preferences. Think of a little form with some checkboxes, and toggling a box will load/unload a particular bit of middleware.

While I am able to technically do this, the means by which I've made it "possible" is horrible.

// *** This is bad ***
// Add the middleware
app.use(myNewMiddleware);

// Grab the last item in the stack, move it to the 3rd position
// (which happens to be after expressInit in this particular case)
// I've also written a line to grab the router and move it to the 
// bottom, but I'm not sure that's any better
app.stack.splice(2, 0, app.stack.splice(app.stack.length-1, 1)[0]);

My objective: find a solid way to add middleware _after_ expressInit but _before_ router to allow for dynamic middleware configuration at any time.

// app.stack ==>
[ 
  { route: '', handle: [Function: query] },
  { route: '', handle: [Function: expressInit] },
  { route: '', handle: [Function: myNewMiddleware] }, // <--- Inject middleware here
  { route: '', handle: [Function: router] }
]

Any thoughts on how bad this idea is are welcome!

Most helpful comment

I may be miss-understanding your need, but seems like you can do this with a replaceable router:

var router = new express.Router();
// Dynamically  constructed routes
router.get('/foo', mw1);

var app = express();
app.use(function (req, res, next) {
  router(req, res, next);
});

process.on('SIGHUP', function () {
  // New router constructed routes
  var r = new express.Router();
  r.get('/bar mw1);
  router = r;
});

Does this get you the same functionality without mucking with the middleware stack?

All 24 comments

We have been using this technique to deal with the exact same use-case:

    var handlers = [customMiddleware1, customMiddleware2];

    function mySuperMiddleware(req, res, next) {
            function run(index) {
                if (index < handlers.length) {
                    handlers[index](req, res, function (err) {
                        if (err) {
                            return next(err);
                        }
                        index += 1;
                        run(index);
                    });
                } else {
                    next();
                }
            }
            run(0);
    };

    app.use(mySuperMiddleware);

in this example, you can control handlers in any way you want. It is just an array of middleware that can change overtime.

you can even create the list of handlers to use per request (probably attaching it into req._handlers object in a previous middleware, and using it from there within mySuperMiddleware.

now, this doesn't necessarily mean we are doing the right thing, but it certainly work.

Tj has been opposed to any stack manipulation as it's an unnecessary maintenance burden. However, there's a connect module that will help you with this, I just don't remember the name. I suggest you find and use that module instead.

@caridy actually has a good idea. What he's done is basically mount another connect/express stack. Aside from manipulation itself, he's compartmentalized the "customizable" portion of the app.

Thanks jonathanong, I'll check it out!

@jonathanong So I haven't actually plugged it in, but this is a connect plugin, and one that requires that all middleware be named in order to do the after and before calls. When express is registering its own middleware, it doesn't name them, so I suspect attempting to do things like before('router') won't work.

I think creating middleware that wraps middleware is the only option it seems.

what do you mean by "its own middleware"? you won't be able to manipulate the router with it. you _should_ be able to manipulate anything via app.use() as long as you name the function.

either way, it's out of scope of this repo :P

Ah, sorry, I mean that core express is the one that calls app.use() on various middleware. Those calls don't include a name, so I cannot sort by it.

But yes, I get it, not in scope. :)

well, if you do app.use(function someName()) it should be named. i would take a PR to name the internal middleware if any are unnamed.

Oh, looking at that library based on the readme I assumed it required you to do app.use(function ()).as('someName'). If that's not true, then we're golden, but I don't see any examples that do that.

I can tell you from looking at the stack a lot that all the middleware functions from express core are named.

oh. if they do, then that sucks!

You're telling me. Don't suppose it's a feature that express would consider absorbing? It'd be pretty neat, you have to admit.... I'll PR it myself if you'll consider it. :)

naw, it becomes too complicated. people are going to inevitable ask for more ways to manipulate it and it doesn't add any core functionality. plus, tj and i are of the opinion that if you have to add/remove middleware during runtime, you're probably doing something wrong.

plus, manipulating an array isn't very difficult to do without a library.

Well, it was worth a shot! Thank you for your consideration.

Without trying to sound like I'm trying to start a debate, I'd be happy to share my particular use case with you regarding dynamic middleware addition/removal if it could possibly change your perspective on its potential usefulness. That having been said, my use case is super atypical, I assume.

But otherwise, no big deal, and have a nice day!

Hello @adamterlson I have a similar use case as yours. What did you end up using?

Altering the routes/stack is not supported. You can simply make your middleware talk to each other over an in-memory bus to get the desired on-the-fly configuration changes, though you want to understand that because Node.js is async, a request could be in the middle of the stack and if it suddenly changed, it would probably result in weird things occurring.

Basically you can just encapsulate all your logic into a single middleware, and implement your own dynamic stack within that.

@recidive I created a really basic bit of middleware management that was "middleware-like", as in it acted just like the express middleware execution system but the stack was dynamic. For example, I wrote my own next method that'd go through and if anything bombed it'd delegate handling that back to the native express next function. I don't have the code available to me anymore (previous gig), but I'd be happy to put together a gist or something if you need additional guidance.

Thanks guys.

@adamterlson, I think I understand your approach. Was it Express 3 or 4? In 4 we have the Router. I believe I can extend it for adding methods to manipulate the stack. But I'd like to see a gist illustrating your approach if you can put this together for me.

Express 3, I believe, if not earlier. I do not see how the router is relevant in this scenario, as you have to register the middleware either in a router or the main app. When I get around to the Gist, I'll reference it
here.

If anyone's interested, I have something slightly different but that can serve as a base for what is requested here: https://github.com/Xananax/connect-url-pipe

I know this has been closed for a while however i would like to ask the question from a sightly different perspective. The idea is that as you are upgrading an application or patching the application it would be nice to be able to reload or add routes to the application without having to bring the server down. Appending a route to the end of router list is quite simple.
one util to find the last router in the stack:
exports.findLastRouterInStack = function(stack){ for( var i=stack.length-1; i>=0; i-- ) if(stack[i].name==='router') return(i+1); return(stack.length); };

and a simple stack manipulation in the use function:
if(layer.name==='router' && this.stack.length>0){ this.stack.splice(findLastRouterInStack(this.stack),0,layer); } else { this.stack.push(layer); }
to extend this to reload a route would be as simple as identifying if there is a matching regexp within the stack. and doing a simple assingment

I may be miss-understanding your need, but seems like you can do this with a replaceable router:

var router = new express.Router();
// Dynamically  constructed routes
router.get('/foo', mw1);

var app = express();
app.use(function (req, res, next) {
  router(req, res, next);
});

process.on('SIGHUP', function () {
  // New router constructed routes
  var r = new express.Router();
  r.get('/bar mw1);
  router = r;
});

Does this get you the same functionality without mucking with the middleware stack?

I am not sure I am completely following your example. However at first glance I would say no. Based on my understanding of your example, all routes would fall under http://:/foo which is not preferable. limits the ability to define paths based on use case or context. Also the idea is to load them for a "hot" upgrade of the system in which case new and updated routes would be added, updated or removed from the stack while the service is up. What is being proposed would mainly be used to manage the routes of a Node, Express application rather than designing the application to load the routes dynamically upon request. The example above to me appears to fit better in the dynamic application use case than the application management use case.

I am pretty sure the general idea is what you want. I think maybe you are getting hung up in the details of my example, which I purposely left minimal. And the rotues could be whatever you needed, using /foo was just an example.

With the above code example you can replace the value of router dynamically, which is was my sighup example was supposed to show. You could do the same logic which is inside of the sighup inside a route handler, or a timer or on code change with a file watcher.

The main point was this:

app.use(function (req, res, next) {
  router(req, res, next);
});

Which shows how you can pass a request from a parent application into a router instance which you can "hot" reload however you want. Does that help?

I realize even a bit more elaboration would be good. In the above example, the application would start out serving the result of the middleware function mw1 on the route /foo. After the process receives the reload signal, sighup, it would then setup a new router with the route /bar serving the result of the mw1 middleware.

With this as your foundation you could have it build dynamic routes, or dynamic middleware however you want. If you are "upgrading an application" as in a zero downtime deploy, you could achieve this by just making your "application" expose the router, then re-require in the application at sighup and hooking in the new router instance and removing the old one.

FWIW, I think this is a poor decision for this use case and that you should not attempt to do this in a production application because it will probably lead to memory leaks or odd error conditions which are hard to reproduce. Instead you should restart your application and achieve zero downtime with any of the other good solutions on the market. But for other use cases and non production scenarios this should work just fine.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Domiii picture Domiii  路  3Comments

AndrewEQ picture AndrewEQ  路  4Comments

afanasy picture afanasy  路  3Comments

haider0324 picture haider0324  路  3Comments

despairblue picture despairblue  路  3Comments