I saw a tweet quite recently that the team behind my preferred web application framework for node.js - Express - has come out with another web framework that is supposedly much better optimised for node, and they have named it Koa.
So why would Koa be better than Express? Put in a relatively simple manner: no more callbacks. As explained on their website, a Koa application is an object containing an array of middleware generator functions.
This begs for the question - what are Generators in JavaScript? To understand this we also need to discuss what Iterators are.
An Iterator is an object that knows how to access items from a collection one at a time, while keeping track of its current position within that sequence (by utilising a method called next()
).
Here's a simple test that you can try out yourself. Note: You need to try these examples out using Firefox's Web Developer Tool the reason for this is that Chrome's V8 engine is an implementation of ECMAScript and not JavaScript and henceforth it does not support these functions. However, if you enable the "Experimental JavaScript" flag in Chrome, you can get some of the functionality but the code examples here may or may not work.
First, let's create a book
collection with multiple elements:
var book = {
era: 'Pre-Republic',
title: 'Dawn of the Jedi: Into the Void',
author: 'Tim Lebbon',
type: 'print',
};
Then we can iterate through the elements using the following piece of code:
var iterator = Iterator(book);
var first = iterator.next();
console.log(first);
//returns 0: "era", 1: "Pre-Republic"
var second = iterator.next();
console.log(second);
//returns 0: "title", 1: "Dawn of the Jedi: Into the Void"
You can also utilise Iterators in a for..in
statement:
var langs = ["English", "Italian", "Spanish"];
var iterator = Iterator(langs);
for (var lang in iterator)
console.log(lang);
[object Array] //0: 0, 1: "English"
[object Array] //0: 1, 1: "Italian"
[object Array] //0: 2, 1: "Spanish"
A Generator is a better way to build Iterators - it's a special type of function that works as a factory for iterators. A function becomes a generator if it contains a yield
expression. When a generator function is called the body of the function does not execute straight away; instead, it returns a generator-iterator object. Each call to the generator-iterator's next()
method will execute the body of the function up to the next yield expression and return its result.
As an example (don't forget that you can only test this in Firefox's Web Developer Toolbar):
function testGenerator() {
for (var i = 0; i < 3; i++)
yield i;
}
var test = testGenerator();
console.log(test.next()); //returns 0
console.log(test.next()); //returns 1
This should be enough information for the time being, if you seek further knowledge please consult MDN.
Let's get back to the original topic - that is Koa. To demonstrate it's capabilities I have put together a REST service that allows users to retrieve information about Star Wars books.
The code does not support the full CRUD functions, only reads. My choice for the backend database fell on MongoDB (again). Unfortunately enough at the time of writing this article, Mongoose (my preferred MongoDB object modelling tool for Node.js) does not support yield
and it does not work with Koa very well. I had to look for a different library and I found monk. I have left the whole code as simple as I could:
var koa = require('koa'),
route = require('koa-route'),
app = (module.exports = koa()),
monk = require('monk'),
wrap = require('co-monk'),
db = monk('localhost/starwars'),
books = wrap(db.get('books'));
app.use(route.get('/book', list));
app.use(route.get('/book/:title', show));
function* list() {
var res = yield books.find({});
this.body = res;
}
function* show(title) {
title = decodeURI(title);
var res = yield books.find({ title: title });
this.body = res;
}
/*app.get('/book/:title', function(req, res) {
title = decodeURI(req.params.title);
book.find({title: title}, function(error, book) {
res.send(book);
});
});*/
if (!module.parent) app.listen(3000);
(The above code available on GitHub.)
This is probably the easiest and most straight-forward REST service that you've seen. Just for comparision purposes I have added a comment showing how the same code would look like in an application using Express. The difference is quite shocking isn't it? Using asynchronous operations without callbacks makes the code look a lot more easier to understand and maintain.
This article wouldn't be full without explaining how you can run the code example. yield
and other Generator/Iterator related functionality is only available in Node.js version 0.11.3 and above. Now, if you're like me and don't want to run an unstable version of Node.js, I have good news for you: you can run multiple versions of Node.js at the same time, just follow these steps:
curl https://raw.github.com/creationix/nvm/master/install.sh | sh
nvm install 0.11.3
nvm use v0.11.3
to switch to the desired versionTo have Generators and Iterators app.js
has to be invoked with a special flag: node --harmony-generators ./app.js
.
That's all folks. I hope you've enjoyed this post. I encourage you to have a peek at the code - I am giving away the whole dataset as well, and here are some setup instructions to get you going.