Replacing Express with Koa - an experimental REST API
Older Article
This article was published 12 years ago. Some information may be outdated or no longer applicable.
I spotted a tweet recently. The team behind my preferred Node.js web framework, Express, had released another framework called Koa. Supposedly much better optimised for Node.
So why would Koa beat Express? Put simply: no more callbacks. A Koa application is an object containing an array of middleware generator functions.
That raises a question. What are Generators in JavaScript? To answer that, we need to talk about Iterators first.
An Iterator is an object that knows how to access items from a collection one at a time, tracking its current position through a method called next().
Here’s a simple test you can try yourself. Note: You’ll need Firefox’s Web Developer Tool for these examples. Chrome’s V8 engine implements ECMAScript (not JavaScript proper), so it doesn’t support these functions. You can enable the “Experimental JavaScript” flag in Chrome for partial support, but the examples here may or may not work.
First, 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 iterate through the elements:
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 use 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 function that acts as a factory for iterators. A function becomes a generator when it contains a yield expression. Calling a generator function doesn’t execute the body right away. Instead, it returns a generator-iterator object. Each call to next() runs the function body up to the next yield expression and returns that result.
An example (remember, Firefox Web Developer Toolbar only):
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
That should be enough background. For more detail, check MDN.
Back to Koa. To show what it can do, I’ve built a REST service that retrieves information about Star Wars books.
The code only supports reads (no full CRUD). I picked MongoDB for the backend database. Unfortunately, Mongoose (my usual go-to for MongoDB object modelling in Node.js) doesn’t support yield and doesn’t play well with Koa at the time of writing. I found monk instead. I’ve kept everything 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 is available on GitHub.)
That’s probably the simplest REST service you’ve seen. I’ve left a comment showing how the same code would look in Express. The difference is striking. Asynchronous operations without callbacks make the code far easier to read and maintain.
Here’s how to run the example. yield and Generator/Iterator functionality requires Node.js 0.11.3 or above. If you don’t want to run an unstable version of Node (I didn’t either), you can run multiple versions side by side:
- Install the Node Version Manager:
curl https://raw.github.com/creationix/nvm/master/install.sh | sh - Install the version you need:
nvm install 0.11.3 - Switch to it:
nvm use v0.11.3
To enable Generators and Iterators, invoke app.js with a special flag: node --harmony-generators ./app.js.
That’s it. I hope you enjoyed this. I’d encourage you to peek at the code. The full dataset is included, and here are some setup instructions to get you going.