Analyzing Connect Js

When I heard the word “middleware” , I often associate it with the bloated stack from Java world. However from wikipedia, a middelware means “computer software that provides services to software applications beyond those available (from the operating system)”. So it is not tight to specific technology. In Node.js, I usually use Express Js which utilizes Connect middleware framework. While I know that Connect contains a lot of middleware (useful services), I didnt really know how it works internally. So last week I had “analysis session” with my friend at night. Both of us read Connect’s files, and much to our surprise it is not as complex as we expected. It is elegant yet simple. Basically Connect wraps Node.js’ http.createServer method and handles the incoming request. This is achieved via listen method of Connect.

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};

We can see that “this” reference is passed in http.createServer method, and this refers to connect instance, a function which calls its own handle method.

function app(req, res){ app.handle(req, res); }

Connect also has “use” function which is needed for adding a middleware to its pipeline.

connect()
  .use(connect.logger('dev'))
  .use(connect.static('public'))
  .use(function(req, res){
    res.end('hello world\n');
  })
 .listen(3000);

The middleware provided is a function with the following signature

function middlewareName(req, resp, next){
    //do something
}

The “next” parameter is needed for async continuation.

From these info, it is clear that Connect provides a pipeline for executing Node.js request. Once again we see pipeline pattern is suitable for web application.

We can illustrate the connect processing with this mini example. Listen function is removed for simplicity.

function connect(){

    function app(req, res){ 
        app.handle(req, res); 
    }
    
    app.use = function(middlewareFunc){
            app.stack.push(middlewareFunc);
            return this;
    };
    app.handle= function(req, res){
        var index =0;
        function next(){        
            if(index < app.stack.length){
                var func = app.stack[index++];
                func(req, res, next);
            }
        }
        next();        
    }
    
    app.stack =[];    
    return app;
};

Lets say we have 2 middlewares for parsing request body and cookie.

function bodyParser(req, resp, next){
    console.log('parse body');
    next();
}

function cookieParser(req, resp, next){
    console.log('parse oookie');
    next();
}

Finally we call connect in application code

var app = connect();
var request ={}, response ={};
app.use(bodyParser).use(cookieParser)
.use(function(req, resp, next){
    console.log('render html');
})(request, response);  //this line replaces listen method