JSGI vs Connect for Node Middleware

By on June 11, 2010 4:08 pm

JSGI (JavaScript gateway interface) is the broadly adopted standard for server-side JavaScript web applications and middleware. JSGI is designed specifically for ease of use in asynchronous, evented environments, and consequently JSGI has been a perfect fit building applications on Node. JSGI-Node has existed for sometime as lightweight JSGI server/adapter for running JSGI-based applications on Node. However, “Connect” was recently released as a new alternate middleware system for Node. This spurred me to make sure JSGI-Node was optimized, and compare the Connect middleware design to JSGI.


To begin with, I did some performance comparisons. The first test is the simplest possible “hello world” application. This was run against a mock server to eliminate any overhead of HTTP parsing and isolate the test to simple request delegation. Here are the results in requests per second:

JSGI-Node: 730K/s
Connect: 204K/s

Here JSGI-Node is shown to be about 257% faster. Next, I created a stack of 9 middleware apps in front of the “hello world” app. These middleware apps do nothing but delegate to the next layer. This was designed to isolate the middleware delegation mechanism. The results are in middleware delegations per second:

JSGI-Node: 80,600K/s
Connect: 2,230K/s

Middleware delegation is over 3000% faster with JSGI than Connect! Why? JSGI is based on a simple, lightweight, pure JavaScript approach to connecting middleware. Middleware apps are simple function closures that accept another app that they can delegate to. Nothing is actually required to wire middleware together. Connect on the other hand has to go through a heavy delegation layer to wire the middleware together. Middleware delegation might not pose a significant bottleneck for simple applications, but as middleware layers accumulate, the speed of delegation becomes increasingly important.

Putting these test together, the full request delegation and middleware delegation through nine middleware layers to a simple “hello world” app results in following requests per second:

JSGI-Node: 675K/s
Connect: 112K/s

With JSGI providing about a 503% better performance with isolated request and middleware delegation.

Here are the test files.


One of the powerful aspects of JSGI is that middleware is connected by simply passing apps as arguments. This means you can not only create linear stacks, but you can create rich branching tree type request routing structures. Connect on the other hand is really only designed for creating linear middleware stacks. This is useful for demonstrating simple middleware integration, but often real applications require much more sophisticated logic than these simple stacks. While Connect comes with routing middleware, it is not clear if it is possible to create middleware stacks under the different routes (especially with the same ease as JSGI).

Layer Isolation

One of the key concepts of middleware is layering. JSGI provides a simple mechanism for allow each layer to provide a controlled view of the request for the next layer and control the response from that layer. On the other hand, Connect does not provide any mechanism for wrapping and creating new request objects or handling response objects. Altering the view for the next layer means actually altering the request and response objects in place. This fundamentally breaks the layering concept of middleware because when the request object is altered, it is altered across all layers, below and above, not just for the next layer.

Ease of use

Ease of use is in the eye of the beholder, so I will just show so code comparisons. Here is a sample of how you create middleware with JSGI:

function Middleware(nextApp){
  return function(request){
    var response = nextApp(request or our own request object);
    return response or any modified response we want to return;
// now wire it up

And in Connect:

Middleware = {
  handle: function(req, res){
    // must modify req in place
    // modify res handlers in place as well
    // restore any res handlers that might be used lower down in the stack
// now wire it up
var connectServer = new Server([
  {module: Middleware},
  {module: app}

Pintura has a wealth of examples of other middleware applications.

One complaint that has been made about JSGI is that it was not designed for specifically for Node. However, it was designed for asynchronous event-oriented architecture, which is exactly the point of Node, and so there really is no credibility to discounting JSGI for not being Node specific. It is a perfect fit for Node.

Middleware Availability

Simply take a look at the rapidly growing list of middleware modules for Node to see what is available for JSGI. JSGI is based on a collaborative effort of large number of participants in the CommonJS group. Furthermore, JSGI-Node is licensed under the Dojo foundation, which has the known for rock solid CLA-protected IP-encumberance-free liberally licensed software with a track record of being safe to use.

Now to be fair, Connect is about more than just inventing yet another middleware interface. It has some other extremely useful features including a powerful command line startup mechanism and middleware configuration techniques. In fact, Tim Caswell has actually talked about possibly including support for JSGI in Connect. This would be a great addition, and would allow Connect to actually combine the superior JSGI middleware interface with the other cool features in Connect.

The elegant design of JSGI, based on simple idiomatic JavaScript, allows for simple, intuitive middleware, with flexible connections, incredible performance, and connected to a growing ecosystem of reusable appliances.


  • Great article, Kris. I appreciate all your work on JSGI and CommonJS

  • Kris,

    Your point is probably valid, but this test doesn’t support it:

    (1) It writes a string to the response for Connect, but not JSGI
    (2) It spawns a node.js server for Connect (using a private API!), but not JSGI
    (3) It assumes sync, but if I recall correctly Connect is async. You should be running the test async, and looking for when the last response.end() is called, not when the loop ends.

    Looking forward to a more accurate test!


  • @Jed:
    (1) You are right, I updated the tests for writing an equivalent string. The initial request delegation is 257% faster instead of 290%.
    (2) A server object is created, but it never starts actually listening to a socket or parsing requests, and it is completely outside the actual benchmark loop. It is irrelevant.
    (3) It is true that I didn’t test async, the point of this benchmark is to isolate the request and middleware delegation phases. In fact it would be quite difficult to test async with the current design, because as I pointed out, Connect really doesn’t offer any true way to layer/wrap request/responses, it just successively mutates the request/response, so there is no valid apples to apples comparison of async.

    The tests should be accurate now, thank you.

  • Great article. JSGI is much more elegant and simple, not to mention portable: your code can run on Narwhal, Node, and the *fantastic* Ringo (www.ringojs.org) containers…

  • Great writeup, you’ve given me reason to speed up the delegation loop. I do hope were able to collaborate on projects even with fairly different goals. Thanks for providing the code for the tests too.

  • @Kris: Very cool, thanks for the update! I’m firmly in your camp re: the use of closures. Good to see that they pay off, and hopefully we’ll see people get more comfortable with this style.

  • randomdev

    Thanks for posting this, very interesting!
    Have there been any developments since the original post? I’m having trouble finding information on what the state of the art is in this (rapidly evolving) field, so would appreciate some pointers.