4 min read
Next in trending

Working with Koa and Generators

Early experiences building an API in Koa

Working with Koa and Generators

Early experiences building an API in Koa


Three months ago I began working with the Applications Team at the Danish startup, Mojn. At the time Mojn’s technical body consisted of a number of disconnected undocumented API’s of various forms and features.

The main task of my team was to start building business apps. To solve that problem we felt we needed a centralized API, using HTTP and JSON.

We quickly got started and built our API in Express. Things were looking good. It is solid, fast and fairly easy to maintain.

Being an avid user of Express and that whole family of libraries, I’ve been eyeing Koa for a while and once it was announced as stable and production ready, I couldn’t wait to use it.

I managed to convince the rest of my team that this was a worthwhile idea and thus I began refactoring our 3 months of Express. I’d like to tell you how we started working with Koa.


var koa = require('koa');
var app = koa();

app.use(function* (next) {
yield next;
this.body = 'Hello World';
});

app.listen(3000);

Meet the Koa Hello World application. Exactly what Koa is…simple. If you’re migrating from Express, like the majority of eventual Koa users will, one of the most important things you should know is that it’s an attempt at building the most simple abstraction of Express.

To achieve the simplification of something that is already so simple, obviously something has to be significantly different. In the case of Koa, this was possible by using the generator feature in ES6, enabled in Node.js 0.11+, when invoking the interpreter with the —harmony-generators flag.


Generators are a new kind of JavaScript object that yield values instead of returning a value.

function* generator(a) {
console.log('yielding 1 + a');
yield 1 + a;
console.log('yielding 2 + a');
yield 2 + a;
}

var gen = generator(1);
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }

The nature of a generator is that the execution of its function body is suspended until the next value of the generator is requested by invoking the .next() function on the generator.

This means that not only before the first .next() is called, but after every yield statement, the execution is suspended, giving JavaScript native control flow.

Before Ajax and Node, no one used JavaScript for asynchronous operations, and the well known fn(err, res) callback signature was invented to create an artificial flow control.

Think of generators as a sink that you can drain.


So how do we go about using the yield statement to control the flow of our asynchronous code? Enter co.

Koa uses co to sort of empty the drain of its middleware stack. In Express, the middleware stack was linear and you were in charge of explicitly calling the next middleware until the stack was empty and hopefully a response had been written to the response stream. In Koa, you yield control of the flow to the next middleware and wait until the flow returns to you.

An imagined Koa middleware control flow.

This effectively creates first an upward flow and consecutively a downward flow of control; it is ideally where the content of the response is determined at the peak. This behavior is useful, because now middleware can serve an extended purpose, both before the peak and after the peak.


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 response-time middleware is an example of perfect utilization of the returning flow behavior and allows us to pack related functionality in a single middleware.


Advancing from Express, one of the major differences is how routing is managed. Express came with a built-in router middleware, that figured out which route to sent the request to; this behavior was confusing to many, because the controllers had the same signature as the middleware and the router was automatically added to the stack once you added your first route.

As a part of the simplification, Koa does not come with a router; it’s agnostic and it’s up to you.

To solve this problem at Mojn we choose to use koa-route and add each route as its own middleware.

var route = require('koa-route');
var routes = [
route.get('/hello', function* () { this.body = 'Hello World'; })
];
routes.forEach(function (route) { app.use(route); });

In fact, we have a routes.js file, which is an export of an array literal of routes.

With 50 routes, this obviously adds 50 middlewares to the stack. Effectively it isn’t that much different from using a router that contains all your routes since internally that router would loop over every route until it found a match.


Building an API doesn’t stop at middleware and routes, but in the spirit of Koa, I’d like to keep things simple.

Edited by Victoria Ann Vele.