Is Koa the Future of Node Frameworks?

09 Jan 2014 | By Alex Young | Tags node modules es6

Are you tired of callback wrangling for web middleware? Koa (GitHub: koajs / koa, License: MIT, npm: koa) is a new web framework by TJ Holowaychuk, Jonathan Ong, and Julian Gruber that embraces ES6 generators down to the core. Gone is the next parameter in Express, replaced by yield next which can be called in a synchronous style.

With generators we can achieve “true” middleware. Contrasting Connect’s implementation which simply passes control through series of functions until one returns, Koa yields “downstream”, then control flows back “upstream”.

You’ll need Node 0.11.9 or above to use Koa, and the node command must be invoked with the --harmony flag. The use of generators is partly enhanced by TJ’s Co module, so if you’re not intimately familiar with ES6 promises be aware that Co is used.

Here’s an example: the app.callback method returns a handler that is compatible with Node’s http.createServer method, and uses co:

app.callback = function(){
  var mw = [respond].concat(this.middleware);
  var gen = compose(mw);
  var fn = co(gen);
  var self = this;

  return function(req, res){
    var ctx = self.createContext(req, res);
    onSocketError(ctx, ctx.onerror);
    fn.call(ctx, ctx.onerror);
  }
};

This example is from Koa’s source, and allows the following to work:

var koa = require('koa');
var app = koa();
app.listen(3000);

// Equivalent: http.createServer(app.callback()).listen(3000);

The Request Response Pattern

The key to making solid Node web applications is to realise and exploit the fact Node speaks HTTP. Contrast this to PHP or ASP, where “pages” execute and generate output. Those req and res objects are there for a reason: Express and similar frameworks are built on Node’s http core module, and the http module’s API is based around these request and response objects.

I like thinking in terms of HTTP requests and responses – it gives me the sense that I’m dealing with HTTP “functions” that take input and transform somehow. That makes it easier for me to test my web apps, and I’ve been comfortable working this way since I discovered Express.

In Koa things are slightly different:

A Koa Context encapsulates node’s request and response objects into a single object which provides many helpful methods for writing web applications and APIs.

While it’s true that Express decorates the request and response objects, Koa goes further by abstracting them. You can still get at the request and response, and there’s also a Koa request and response as well:

app.use(function *(){
  this; // is the Context
  this.request; // is a koa Request
  this.response; // is a koa Response
  this.req; // Node's request
  this.res; // Node's response
});

Notice that this is significant – you may have seen something similar in jQuery callbacks. I think it was clever of Koa’s authors to execute middleware from within a “context”, because it makes sense semantically. The current context has aliases to commonly accessed request and response properties, so the average Koa middleware has less indirection and looks lean and clean.

Understanding Yield

I realise that the advantages of generators in middleware might not be obvious at first. The best example I can think of from Koa’s documentation is measuring response time. This example yields early, allowing the response time to be measured:

// x-response-time

app.use(function *(next){
  var start = new Date;
  yield next;
  var ms = new Date - start;
  this.set('X-Response-Time', ms + 'ms');
});

The start time is recorded, then execution is deferred to the next item of middleware in the stack with yield next. This all “unwinds” at the end of the request, so as long as the x-response-time was the first item, execution will continue after yield next. That means var ms = new Date - start; will correctly measure the elapsed time.

I think many of us have struggled to stack middleware in meaningful ways, and this could make our code more readable. Think about it like this: a single function can encapsulate the entire intent of the middleware.

Future

It might be that the dependence on Node 0.11 and our investment in existing frameworks will hold back Koa’s adoption. However, remember that you can mount entire applications as middleware in Connect, so what’s to stop you from mounting a Koa app inside your existing Express apps, once generators are standard in your production version of Node?

Even if you don’t switch to Koa, you should start thinking about how to use generators, and I think middleware is a sensible place to explore their implications.


blog comments powered by Disqus