June 14, 2016

Introduction to Koa.js

By Jscrambler | 5 min read

Introduction to Koa.js

If you’re using Node.js to build an application that listens over HTTP — a web server, web application, REST API for example — then chances are you’ll reach for Express, perhaps without even really thinking about it. Not without reason; it’s probably fair to say that it’s the “go to” package for such purposes, and indeed it’s one of the most depended on packages in the Node eco-system.

However, there are alternatives to Express. One such option is koa.js, a “next generation web framework for node.js”. In this article, we’ll take a look at it, so that you can decide for yourself whether it’s a valid alternative or one you might like to try in your next project.

What is Koa.js

Koa.js is actually by the team behind Express, and in its own words “aims to be a smaller, more expressive, and more robust foundation for web applications and APIs”.

Its key feature is the use of ES6 generators. In practical terms this means that an application written using Koa.js contains far fewer callbacks. Behind the scenes it still uses the asynchronous goodness we’ve come to expect from Node.js, but the code itself looks markedly different — and arguably much cleaner, and easier to understand. Judge for yourself when we come to look at some examples.

Hello World

Let’s implement the obligatory “Hello World”:

// server.js
var koa = require('koa');
var app = koa();

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

app.listen(80);

Pretty straightforward; you’ll soon become accustomed to the fact that everything in Koa is middleware; app.use() is used extensively. You’ll note that there are no callbacks, and that we can set the body of the response on the application object via the body property.

Depending on the version of Node you have installed, you may need to use the --harmony-generators flag when running it:

node--harmony - generators server.js

Rather than type this out each time, you can use the scripts property in your package.json file like so:

{
    ...
    "scripts": {
        "start": "node --harmony-generators server.js"
    }
    ...
}

Now you can run it simply by typing:

npm start

Now, let’s dive a little deeper.

Routing

Integral to any web application or API is routing. One prominent characteristic of Koa.js is that it deliberately provides the minimum of functionality out-of-the-box, so we’ll need to install some additional middleware.

The koa route package provides middleware for routing; that is, mapping URLs to methods.

Install it using Npm:

npm install koa - route--save

Let’s modify our “Hello World” so that it returns the message for the URL /hello:

app.use(route.get('/hello', hello));

function* home() {
    this.body = 'Hello World';
}

Here’s a slightly more advanced example, which demonstrates the use of route parameters:

app.use(route.get('/', home));
app.use(route.get('/page/:id', page));

function* home() {
    // Render the homepage
}

function* page(id) {
    // Render a page with the specified id
}

In order to actually implement these methods, it’s time to take a look at templating.

Try Jscrambler For Free

Templating

In order to add templating functionality, we’re going to use the co-views package, which in turn uses the co for generator-based control flow.

One of the great things about the co-views library is that it allows you to use one of many templating engine libraries — of which there are a great many options! — or even to use multiple engines in a single project. It also enables you to map file extensions to implementations.

Whichever engine you decide to use, note that you’ll need to make sure you install it separately; co-views doesn’t do that for you.

To illustrate how to use co-views, start by installing it along with a templating engine — in this example, ejs:

npm install co - views ejs--save

Then require it:

var views = require('co-views');

Now we can use the views method to set up a render() method, by supplying some configuration values.

In the following example we’re specifying the location of our templates — a folder named views — and we’re “mapping” files with the .html extension to the ejs engine:

var render = views(__dirname + '/views', {
    map: {
        html: 'ejs'
    }
});

To render a view for a given route, you can do this; note that this assumes you have a template named views/index.html:

app.use(route.get('/', home));

function* home() {
    this.body =
        yield render('index');
}

Additionally, you can pass a hash of data as the second argument to render(), for example:

this.body =
    yield render('page', {
            title: 'Page title',
            body: 'The body of the page');

A typical page template might look like this:

<%- include( 'partials/header.html' ) -%>
    <h2><%= page.title %></h2>
    <%=p age.body %>

        <%- include( 'partials/footer.html' ) -%>

We can use this principle along with route parameters in a more complete example, albeit one with a static hash of “pages” rather than, say, something database driven. Take a look at this example:

var pages = {
    about   :    {
        title: 'About',
        body: 'This is the about page'
    },
    contact   :    {
        title: 'Contact',
        body: 'This is the contact page'
    }
};

app.use(route.get('/', home));
app.use(route.get('/page/:id', page));

function* page(id) {
    var page = pages[id];
    if (!page) this.throw(404, 'No such page');
    this.body =
        yield render('page', {
            page: page
        });
}

This also demonstrates the throw method, which allows you to issue an error HTTP status; in this case a 404 not found.

However, this will only throw a 404 when the route is prefixed with “page”; we’re also going to need a “catch-all” handler for routes which haven’t been defined. We’ll look at that in the next section.

Handling 404 Errors

One very important piece of functionality for web applications is handling page not found errors. We can do so by creating some simple middleware.

The code below shows how you might approach this with koa.js:

app.use(function* pageNotFound(next) {
    yield next;

    if (404 !== this.status) return;

    // Explictly set the status code
    this.status = 404;

    this.body =
        yield render('404');
});

Here we’re checking the status attribute of the application for the 404 status code; if we are indeed to handle a page not found, then we render out the 404.html template and tell koa.js to explicitly set the status code to 404, so that it doesn’t assign a status code of 200 to the rendered error page.

Here is a more advanced page not found handler, which customizes the output according to the request’s accept header:

switch (this.accepts('html', 'json')) {
    case 'html':
        this.type = 'html';
        this.body = '

        Page Not Found

            ';
        break;
    case 'json':
        this.body = {
            message: 'Page Not Found'
        };
        break;
    default:
        this.type = 'text';
        this.body = 'Page Not Found';
}

Summary

In this article, we’ve taken a brief look at Koa.js, an alternative to Express which leans heavily on generators.

It’s arguably easier to understand and debug than Express thanks to the lack of messy callbacks, which might be a convincing reason for you to give it a try. On the flip-side, because it’s younger than Express it doesn’t yet have the wide range of compatible packages available.

If you want to find out more about Koa.js, refer to its website, find it on Github or or check out this repository of examples. There’s also a guide, FAQ, wiki and a Google+ community.

Lastly, if you want to secure your JavaScript source code against theft and reverse-engineering, you can try Jscrambler for free.

Author
JscramblerThe leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.
View All Posts

Subscribe to our weekly newsletter

Learn more about new security threats and technologies.

I agree to receive these emails and accept the Privacy Policy.