KoaでWeb開発: npmとKoaのミドルウェアで自分だけのフレームワークを作る

こんにちは皆さん
今回はKoaを理解するために、よくあるWEBアプリケーションでも作ってみようと思います。

最近はオープンソースによるパッケージが充実し、パッケージ間の依存性の管理を容易に行えるツールも完備されてきています。
これらのパッケージをうまく組み合わせて、必要な実装だけ行うやり方が、最近の主流のようです。いわゆるフレームワークというものも、最低限の仕組みだけ用意し、後は好きな様にパッケージを組み合わせて使う、スリムな形式のものが増えてきました。
Koaはそんなスリムなフレームワークの一つです。

では、Koaを使ったオーソドックスなWEBアプリケーションの作成をシミュレートします。

# 環境情報

今回は少し環境情報を書いておきます

* CentOS 65
* Node.js 5.3
* npm 3.3.12

# まずはサーバを作る

## プロジェクトの開始

まずは最も簡単なWEBサーバを作成します。
一番最初なので、プロジェクトの作成から行います

```
$ mkdir koa_prac
$ cd koa_prac
$ npm init
```
これで、プロジェクトの作成とnpmによるパッケージ管理を始めることができます。

## サーバの作成

koaを使ったサーバを作るので、なにはともあれ、koaをインストールします

```
npm i -S koa
```
このコマンドを使うことで、koaをインストールしつつ、package.jsonを更新することができます。PHPで言うところの`composer require <package>`ですね。
早速`app.js`を書きます

```app.js
"use strict";

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


app.use(function* () {
  this.body = 'hello world';
});

app.listen(3000);
```
これで、`node app.js`を叩くだけで、サーバが起動します

# ラウターを設定する

## ラウターの導入

しかし、これではどんなURLを打っても、同じものが表示されてしまいます。
そこで、URLに応じて処理を変えるために、ラウター(router)を導入しましょう。
[Koaのwiki](https://github.com/koajs/koa/wiki)では、Koaを使用するにあたって、便利なパッケージがまとめられていたりします。
ここから適当にパッケージを拾ってくることにしましょう。

```
$ npm i -S koa-router
```
これでKoaのラウターが使えるようになるので、次のように実装できます。

```app.js
"use strict";

const koa = require('koa');
const router = require('koa-router')();
const app = koa();

router
  .get('/', function* () {
    this.body = 'hello World';
  })
  .get('/evening', function* () {
    this.body = 'good evening';
  });


app.use(router.routes())

app.listen(3000);
```
これで、URLごとに異なる処理を実装することができるようになりました。

## ラウターを外に追い出す

しかし、app.jsに処理をどこどこ書いてたら、当然煩雑になってしまいます。
ラウターは外に出してしまいましょう

```config/router.js
"use strict";

module.exports = function(){
  const router = require('koa-router')();

  router
    .get('/', function* (){
      this.body = 'hello world';
    })
    .get('/evening', function* (){
      this.body = 'good evening';
    });

  return router.routes();
}
```
一方、app.jsは

```app.js
"use strict";

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

app.use(require('./config/router')());

app.listen(3000);
```
こんな感じで、随分簡略化できます。
githubにここまでの状態のコードをあげておきます。
https://github.com/niisan-tokyo/scratches/tree/koa_example_routing/nodejs/koa_example

# viewを導入

単純な文字列を返すだけなら楽ですが、普通はHTMLを返すように作りたいものです。
そこで、viewを導入してHTMLを返却するように、処理を作りなおしてみましょう

## viewを導入する

viewの導入もまた、wikiから、なにか便利なパッケージを探して済ますことにしましょう。
[koa-views](https://github.com/queckezz/koa-views)は便利そうですが、たくさんのパッケージを入れまくるので、ちょっと重そうです。
ここは[koa-swig](https://github.com/koa-modules/swig)を導入して、swig形式でビューをレンダリングする機構を導入しましょう

```
$ npm i -S koa-swig
```
これで導入完了です

ビューの設定をapp.jsでやってもいいのですが、せっかくラウターを外に出して軽量化したのですから、ビューの設定も外に出しておきましょう。

```libs/render.js
const render = require('koa-swig');
const path = require('path');

module.exports = function(app) {
  app.context.render = render({
    root: path.join(__dirname, '/../views'),
    autoescape: true,
    ext: 'html',
    cache: false
  });
};
```
で、これをapp.jsに読ませれば良いというわけです

```app.js
"use strict";

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

//ビューの設定を入れる
require('./libs/render')(app);

app.use(require('./config/router')(app));

app.listen(3000);
```
また、router.jsにおいて、書き方が以下のように変わります

```config/router.js

router
    .get('/', function* (){
-     this.body = 'hello world';
+     yield this.render('top/index')
    })

```
また、今後のために、routerの'evening'の部分を'board'に変えておきます。

ここまでのコードをgithubにあげておきます。
https://github.com/niisan-tokyo/scratches/tree/koa_example_render/nodejs/koa_example

# 静的ファイルを置く

## CSSやフロントのJSを置きたい

このままだと、見栄えが悪いので、CSSを置きたくなるところです。
[商用利用できる無料のCSSテンプレートまとめ](http://blog.nest-online.jp/9513)に、いい感じのテンプレートがそろっていますので、ここから引っ張ってきましょう。

ところが、CSSやJSのファイルを置こうとするとき、どのように取得すればよいのでしょうか？
例えば、assetsという名前のディレクトリ配下に、各静的ファイル群を置くとします。http://example.com:3000/assets/css/style.cssで静的にCSSファイルを取得できれば、目的達成となるところです。
今の状態のままであれば、ラウターを使ってファイルを渡す処理を描くところでありますが、ここで便利なパッケージとミドルウェアの仕組みを利用しましょう

## 静的ファイルを取得するミドルウェア

調度良いことに、koa-static-serverという名前のパッケージが有ります

```
$ npm i -S koa-static-server
```
で、これを使ってapp.jsを書き直します

```app.js
"use strict";

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

require('./libs/render')(app);

app.use(require('koa-static-server')({rootDir: 'assets', rootPath: '/assets'}));

app.use(require('./config/router')(app));

app.listen(3000);
```
いかがでしょうか？
`app.use(require('koa-static-server'...`の部分だけが追加されていることがわかるでしょう。
たったこれだけで、/assetsで始まるURLにアクセスした時だけ、静的ファイルを取りに行き、それ以外の時はラウターによる処理を実行するようになります。

結果のコード
https://github.com/niisan-tokyo/scratches/tree/koa_example_staticfiles/nodejs/koa_example

## 層状ミドルウェアの構造

この処理を少し詳しく見てみましょう
以前に、[httpリクエストに対するミドルウェアの層構造について見た](http://qiita.com/niisan-tokyo/items/41eb20adafdae8633442)ことが有りますが、今回の静的ファイルを取得する処理は、まさにこの仕組みを活用しているのです。

現在のapp.js上では、`app.use`メソッドによって、２つの機能がミドルウェアとして登録されています。

1. 「/assets」で始まるURLでリクエストされた場合は、assetsディレクトリにある静的ファイルを返す
2. URLに応じて決められた処理を実行する

`app.use`の登録順が、ミドルウェアの順番であることを以前検証しましたが、上述したミドルウェアは「1 -> 2」の順番で起動します。
「/assets」以外でアクセスされた場合、1は静的ファイルを取得する処理を行わず、次のミドルウェアである2に処理を移します。
しかし、「/assets」でアクセスされた場合、1は静的ファイルを取得しレスポンスとしてそのまま返すため、2の処理に進むことはありません。

つまり、処理が十分完遂できたと判断すれば、以降のミドルウェアに無視して、そのままレスポンスを返すことができ、無駄な処理を省くことができます。
このことは1と2の間に、次のようなミドルウェアを追加することで実証することができます。

```app.js
app.use(function *(next){
  console.log('test');
  yield next;
});
```
ラウターによる処理が必要であれば、サーバのコンソール上に「test」というログが出力されます。例えば「/」にアクセスします。
一方、静的ファイルである「/assets/css/style.css」にアクセスすると、上述したログは出力されません。

# まとめ

今回はKoaを使ったWeb開発について、実際にアプリを作りつつ検証を進めました。
npmを使ってのパッケージの依存性の管理とプロジェクトの管理は、nodeでの開発の主流であり、また、ミドルウェアという層構造の概念を用いて、アプリの外観を組み立てていくのはとても楽しいものだと思います。
まだ、アプリケーションとしては足りないところが多いので、次回はもう少し実用的なレベルまで進めてみたいと思います。

# 参考

[KoaでWeb開発: Koaの検証を通じてミドルウェアの概念を理解する](http://qiita.com/niisan-tokyo/items/41eb20adafdae8633442)
[Home · koajs/koa Wiki](https://github.com/koajs/koa/wiki)
[商用利用できる無料のCSSテンプレートまとめ](http://blog.nest-online.jp/9513)