Koa: Zero to Todo List
Note: you need to run node 0.11 with –harmony to run the code.
The old paradigm
In the standard node library, The ‘http’ module is used to create servers.
1 2 3 4 5 6 7 8
Express exposes a function that we can give to http.createServer as a callback. Express middleware is a set of functions that take 3 arguments, req, res and next. The middleware performs some operations, modifies either the request or response objects and passed down to the next middleware in the stack by calling next(). Its akin to a water flow model where the the response ends somewhere near the end of the middleware stack.
Enter Koa, The generators based framework
Like express, Koa works internally by generating a callback that can be passed to http.createServer(). Unlike Express, it uses generators to provide a much more fine grained model of control flow.
A very basic koa app looks like this, lets make it serve up the contents of a file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Unlike Express, the middleware is written using generators. Downstream middleware in Koa flows upstream on return. Middleware yields down stream by explicitly calling ‘yield next.’. Upon return, the control flow yields back up to where the upstream middleware yielded downstream.
Where Express passes native node req and res objects through to each function, Koa manages a context where it encapsulates them behind an interface. They are still available through the ‘this’ keyword as this.req and this.res. However, Its not reccomended in the docs that you work with these native objects. One could imagine calling this.res.end(”) would throw a monkey wrench in the control flow. Instead you are supposed to work through the this.response and this.request. Most of the methods are aliased directly to this. ‘this.body’ for example, is aliased to this.response.body.
There does not yet appear to be a direct way to get to the request body. The co-body parser accesses the req.body directly so while the docs say don’t do it, Koa is still young so you may have to get your hands dirty.
A Todo app in koa
Now that we’ve covered the basics, lets try something a little more complex. A todo List seems like a good thing no one has ever tried to make before in any technology ever! To simplify assumptions, lets just store the todos in memory.
By itself, Koa is very minimalistic. It does not provide body parsing, sessions, or routing in the core. Unfortunately Koa is still young so there just aren’t that many npm modules for it just yet. A quick search on the Koa website shows that we do have the necessary modules for a basic todo.
- koa-route: for routing
- co-body: for parsing the body of post requests
- koa-static: for serving up static assets
Here’s the basic server side api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
Download the code on github The github version includes frontend code.
A few things of note:
The ‘yield’ keyword ca do some interesting things. If we pass into it an asynchronous function that returns a thunk or promise, it will stop execution of the middleware and wait till it resolves. It then returns the value of the promise or thunk and resumes the generator. This is a hell of a lot easier to read.
A word of caution
The ‘yield’ keyword lets us do some safe blocking but it isn’t always the ideal solution. While the event loop itself isn’t blocked by it the way futures can, it does block resuming of any operations that occur after it.
For example, if we run three asynchronous operations top to bottom that do not depend on each other, like the following…
1 2 3 4 5
This completely defeats the purpose of node’s (almost automatic) concurrency. When we call async1, we are blocking until async2 runs. This is unoptimal. It would be better to get the promises for the three functions and yield on a merged promise.
1 2 3 4 5 6
I’m excited to cut my teeth on some bigger problems. As the framework matures, Its going to allow more fine grained control for how we write the next generation of web applications.