In late 2013, the team behind Express.js announced a new framework called Koa. It uses some features that require an unstable dev version of Node, but in this post I’ll show you how easy it is give it a try both locally and on Heroku.
Before We Start
Whether you play it safe and use a node version manager or live on the edge and download it straight up, you’ll need to get a 0.11.X build of Node. As of writing that’s v0.11.12. Then you’ll want to add these two bits to your package.json file (assuming your main entry point is server.js):
"scripts": {
"start": "node --harmony --harmony_generators server.js"
},
"engines": {
"node": "^0.11.12"
}
With this in place, you’re ready to run on Heroku— you don’t even need to add a Procfile. To run your app locally, use npm start.
Why Koa?
Do a search and you’ll find that most of the focus is on Koa’s use of generators, a feature that’s a part of the upcoming ECMAScript 6 specification. However, there are two other key features; cascading middleware and sane error handling. Getting a superficial awareness of how Koa uses generators will help in understanding its other features.
Yielding to
Koa is built on co, which handles the delegation to generators and gives Koa its nice syntax. You can read about generators, co-routines, and the differences between them if you’re curious.
If you’ve been living in Node and Express for a few years, you’ve either become used to the good ol’ callback syntax:
function(req, res) {
db.getUser(function(err, u) {
if (err) return res.status(404).send(err);
res.send(u)
});
}
or you’ve chosen one of the many paths out of callback hell.
With Koa, you can handle control flow like this:
function *(next) {
var user = yield db.getUser();
this.body = user;
}
With the exception of the *
and the yield
keyword, this almost looks like a “normal” synchronous environment. Feels nice, eh? In this simple comparison it might not seem like a big difference, but if you’ve coded any real apps, you know how easy it is to get tangled in callbacks.
Let’s say your app needs to make two calls out to other APIs to gather data. We’ll write a simple makeCall
function that uses the ever-popular Request module to make HTTP calls. If we try to use the standard version of request
, this wouldn’t work:
var result = yield rekwest('http://google.com');
Instead we need to use co-request; a “co-friendly” version that wraps request
. You can find co-
versions for many popular modules.
Our example will make the two calls in series, one after the other:
var rekwest = require('co-request’);
var makeCall = function*(url) {
var result = yield rekwest(url);
return res.body;
}
var goog = yield makeCall('http://google.com');
var yhoo = yield makeCall('http://yahoo.com');
this.body = {goog:goog, yhoo:yahoo};
To improve our response time, we should make these calls in parallel. Co
(and Koa) have a really simple way to handle this; just yield an array or object, where each element of the array or property of the object is either a generator or a Promise. When Co
encounters this, it triggers all the promises/generators at the same time, waits for the results to return, and keeps things in their correct order.
Here’s a modified version that yields to an array of 2 elements:
var rekwest = require('co-request’);
var makeCall = function*(url) {
var res = yield rekwest(url);
return res.body;
}
var calls = ["http://google.com", "http://yahoo.com"];
this.body = yield calls.map(makeCall);
Here’s a similar version that uses an object with named properties instead of an array:
var rekwest = require('co-request’);
var makeCall = function*(url) {
var res = yield rekwest(url);
return res.body;
}
var calls = {
google: makeCall("http://google.com”),
yahoo: makeCall("http://yahoo.com")
}
console.log(calls);
this.body = yield calls;
Again, we’re using co-request
because request
can’t directly be yielded to. However, it can return a Promise. So if we don’t need to do any parsing in our makeCall
function, and because Co
(and Koa) handles an array or object of Promises in the same way it handles generators, we can do this:
var rekwest = require('request’); //plain old request— not the co-request version
var calls = {
google: rekwest("http://google.com"),
yahoo: rekwest("http://yahoo.com")
}
this.body = yield calls;
You can imagine how short and sweet your web app route functions can become, even if they need to fetch data from a lot of different databases or APIs.
Cascading Middleware
If you’re really into Promises or some fancy control flow library, you might not be impressed thus far. Maybe Koa’s middleware will change that. As explained on the Koa homepage, control “yields ‘downstream’, then control flows back ‘upstream’.” The simple example provided in the Koa docs is logging:
app.use(function *(next){
var start = new Date;
yield next;
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
});
The start
variable holds a timestamp, the function then yields ‘downstream’ to whatever is passed in as next
and when that function (and any functions it yields to) has finished, control is returned back to this function to calculate and log elapsed time.
Have you ever tried building up an HTML response from disparate sources in an elegant way? In Express I always seem to make a mess of that, no matter how well organized my Jade template inheritance and includes are. In Koa, you can do things like:
app.use(function *() {
this.body = "...header stuff";
yield saveResults();
//could do some other yield here
this.body += "...footer stuff"
});
function saveResults*() {
//save some results
this.body += "Results Saved!";
//could do some other yield here
}
Rather than the Express way of carefully building up your HTML string through a series of Promises and callbacks and then finally passing it to res.send()
, Koa allows you to append onto this.body
, and when your middleware stack has gone to the bottom and back up, it sends the response off to the client.
For more examples on ways to compose middleware, see the Koa guide.
Sane Error Handling
You might have noticed the lack of error handling in the example bits of code. That’s not actually an oversight. Whether you want a basic catch-all or granular control, Koa is your friend.
In the case of a quick-and-dirty catch-all, Koa has a great built-in fallback when it encounters errors. Consider this contrived example:
app.use(function *() {
this.body = "...header stuff";
yield saveResults();
this.body += "...footer stuff";
});
function *saveResults() {
// OOPS PROBLEM!
functionThatDoesntExist();
}
Koa catches our blatant error and properly returns a 500 status error to the client. Our app doesn’t crash. It doesn’t need a module like forever to restart it. You don’t need to wrap things in a domain. Of course if your app is in the real world, you’ll want to be more specific, like:
app.use(function *() {
this.body = "...header stuff";
try {
yield saveResults();
} catch (err) {
this.throw(400, 'Results were too awesome')
}
//could do some other yield here
this.body += "...footer stuff"
});
Yes, that’s a bona fide try/catch block. You can call this.throw
with any message and status code, and Koa will pass it along to the client.
Batteries not included
I’ve heard people refer to Express as a micro-framework. Koa has even less built in, but there’s already a nice set of koa and co modules available for routing, body parsing, basic authentication, static file serving, template rendering, and more. You’ll need to add a few more require statements than you did with Express, but just as with Node itself, Koa seems determined to keep the core really lean. For a simple app I built last week, I ended up using:
(for more robust routing, try koa-router)
Although it will take some time for Koa and its underlying technologies to be stable and “production ready,” I have a feeling that Koa will catch on fast. Node’s async callback style is one of the biggest initial hurdles to adoption, and Koa is a pretty slick way to get around it.
Will StrongLoop make use of Koa?
StrongLoop is excited at the potential that Koa presents as a next-generation, lightweight web app framework. The LoopBack API Server currently utilizes Express for its web app functions like routing. What’s LoopBack? It’s an open source API server powered by Node, for connecting devices and apps to data and services. In the future, LoopBack may migrate from Express to Koa for web app functions like server side rendering, routing and web middleware, if the community and our customers ask for it.
We should note that just as Koa takes advantage of ES6 generators for error handling and middleware flow control, StrongLoop’s Bert Belder is working on a similar idea called “Zones“. (Not to be confused with the Angular Zone.js project which shares the same name and some technical characteristics. Yeah, it’s a little confusing, but we are actively working with Brian Ford on how to potentially bring together these two projects for the mutual benefit of the JavaScript and Node communities. Stay tuned!)
Why Zones?
Currently, there are a couple of problems that make it really hard to deal with asynchronous control flow in Node that Zones looks to address. Specifically:
- to always call the callback
- don’t call the callback more than once
- don’t synchronously throw and also call the callback
- don’t call the callback synchronously
Want to learn more about Zones? Read Bert’s blog, get the code or watch the video presentation.