koa.jsを触ってみる

koa.jsを触ってみる。koa.jsはES6(harmony)のジェネレータを多用してコールバック地獄を回避する設計が特徴のNode.js向けWAF。Express.jsのチームが新しく始めたもので、まだまだ安定はしていないみたいだが将来的にはNode.jsのWAFの有力候補となりそう。これは触ってみるっきゃない。ジェネレータの理解がまだ浅いので、間違いがあるかもしれません><

公式サイト

以下のスライドも簡潔に説明されていて良い。

Moongiftにもすでに記事が

ジェネレータについては次の記事が分かりやすい。この記事で扱われているcoはkoa.jsのベースにもなっている。

目次

インストール

koa.jsはNode.jsの0.11.9に対応している。これは最新の開発版で、さらに、--harmonyフラグを付けてnodeを起動しないとダメなよう。 Nodeの安定版(0.10.x)を使いたい場合は、gnodeを使うと良いみたい。 今回は試すだけなので0.11.9を使う。

$ node -v
0.11.9
$ node --harmony hoge.js

パッケージ管理にはnaveを使っていたので、

$ nave use 0.11.9
  • ~/.zshenv

    export NAVEVERSION=0.11.9 #こいつを変える
    export PATH=$HOME/.nave/installed/${NAVEVERSION}/bin:$PATH
    export NODE_PATH=$HOME/.nave/installed/${NAVEVERSION}/lib/node_modules
    export NAVE=$NAVEVERSION
    export NAVE_DIR=$HOME/.nave
    export NAVE_ROOT=$HOME/.nave/installed
    export NAVE_SRC=$HOME/.nave/src
    

とした(naveは微妙・・・という人が多いみたいなので、これからやるならnvmを使ったほうがいいと思う)。

作業ディレクトリを作って、koa.jsをインストール

$ mkdir koajs-play
$ cd koajs-play
$ npm install koa

package.jsonを書いておくと後々便利そう。

  • package.json

    {
      "name": "koajs-play",
      "private": true,
      "description": "koa js play",
      "version": "0.0.1",
      "main": "index.js",
      "scripts": {
        "start": "node --harmony index.js"
      },
      "dependencies": {
        "koa": "0.1.2"
      },
      "engines": {
        "node": "0.11.9"
      }
    }
    
$ npm install

CoffeeScriptが使えるかなー、と思って調べてみたけどまだES6のgeneratorには対応してないみたい。

Hello World

  • index.js

    var koa = require('koa');
    var app = koa();
    
    app.use(function *(){
      this.body = 'Hello World';
    });
    
    app.listen(3000);
    
$ npm start

> koajs-play@0.0.1 start /path/to/koajs-play
> node --harmony index.js

ブラウザでhttp://localhost:3000を開いてHello Worldと表示されれば成功。せっかくなので、単純にジェネレータをyieldしてみる。

  • index.js

    var koa = require('koa');
    var app = koa();
    
    app.use(function *(next){
      this.body = 'Hello';
      yield next; //ここで次のジェネレータを呼び出して自分は止まる
      this.body = this.body + ' !';
    });
    
    app.use(function *(){ //yieldで呼び出される
      this.body = this.body + ' World';
    }); //終了したら前のものを再開。
    
    app.listen(3000);
    

http://localhost:3000Hello World !と表示される。

通常の同期APIを呼ぶように、手続きをずらっと並べて定義できるので分かりやすい・・・かな? でもコールバックのネストよりはスッキリしている。

実行順どうなってるの・・・

上記程度のコードなら流れを追いやすいけれど、ちょっと長いコードになってくると予備知識無しでは実行順序がわけわからなくなってくる。 そこで、実行順について次のページでビジュアル的に説明してくれている。

フロントエンドの技術者向けに、JSイベントにたとえて説明されている。

  • yield next;の前のコードはキャプチャフェーズ
  • yield next;の後のコードはバブルフェーズ

koa.jsではJSイベントを「コンテキスト」と言い換えられる。

これはNode.jsのrequestresponseプロパティを持つContextオブジェクトで、app.useに渡すジェネレータ生成関数の中でのthisはこのContextオブジェクトとなっている。 ミドルウェアの追加によってこのコンテキストの書き換えを制御するのがkoa.jsの中心概念、と考えてもいいと思う。

コードの上から順にコンテキストが降りてきて下まで行ったら上にもどっていって、その流れの中でミドルウェアによってコンテキストが変更されていく。そんな感じ。

コンテキストを操作するためのメソッドも豊富に用意されている。

ミドルウェア

koa.jsでは上記のジェネレータを使ったコアは小さく構成されていてhttpの基本機能を抽象化しており、 ルーティングやビュー、セッションなどの仕組みはミドルウェアとして提供されている。

ミドルウェアは次のページにリストされている。

また、よく使うミドルウェアを集めたnpmパッケージもある。

これをpackage.jsonに書いておけば基本的な用途には足りるはず。

なんというか小さなモジュールを組み合わせるのがいまどきのJSっぽい。ミドルウェアの作り方も次のページに詳しい。

ミドルウェアは単にジェネレータGeneratorFunctionを返す関数となるので、最小のミドルウェアとしてHello Worldの表示をミドルウェア化してみると次のようになる。

// HelloWorldミドルウェアの定義
function *helloWorld(next){
  yield next; // `yield next;` しておかないと次のミドルウェアが呼ばれないので注意。
  this.body = 'Hello World'; //レスポンスボディを設定
};

// HelloWorldミドルウェアをアプリから使う
app.use(helloWorld);

このミドルウェアの開発や組み合わせが、koa.jsを使った開発のキモになるよう。機能的にはkoa.jsはExpress.jsの後継というよりは、ミドルウェアの組み合わせでもっと柔軟な開発ができるようなWAFになっている。

まとめ

以上、koa.jsを触ってみてなんとなくだけどどういうものか理解できた気がする。 ソースももっと追ってみたいんだけど、ジェネレータとco.jsを理解しないとだなあ。

まとめると、koa.jsは

  • yeildを使って非同期プログラミングのコールバック地獄を回避できる
  • でもジェネレータの呼び出し順がちょっとむずいかも
  • ミドルウェアの組み合わせで柔軟なウェブサーバー開発が可能

ということになるかと。。