Koa v1からv2へのアップデート

Koa v1.2.1で書かれたアプリケーションをKoa v2.0.0にアップデートしたのでそのログ的なものです。

v1からの大きな変更点として、middlewareのインターフェースがgenerator functionからasync/awaitへと変わっています。

# 環境の用意

Koa v2ではasync/awaitが使える環境が必要となります。現状Node.jsでasync/awaitを使うためには主に

- Node.js v7系を使い `--harmony-async-await` オプションをつけて起動
- Babelでトランスパイル&polyfill

という二種類の方法がありますが、ここでは後者のBabelを使う方法で進めていきます。

```sh:pluginとpolyfillのインストール
$ npm install babel-polyfill babel-plugin-transform-async-to-generator
```

既にクライアント側でBabelを使用していて、presetやpluginsの設定を分けたい場合は[envを設定](https://babeljs.io/docs/usage/babelrc/#env-option)します。

```json:.babelrc
{
  "env": {
    "client": {
      "presets": [ "es2015", "react" ],
      "plugins": [
        "add-module-exports",
        "transform-class-properties",
        "transform-object-rest-spread",
        "transform-export-extensions"
      ]
    },
    "server": {
      "presets": [ "es2015" ],
      "plugins": [
        "transform-async-to-generator"
      ]
    }
  }
}
```

`BABEL_ENV`を指定した上で[babel-node](https://babeljs.io/docs/usage/cli/#babel-node)を使って動かします。

```:scriptsに追加
"start": "BABEL_ENV=server babel-node index.js"
```

なおパフォーマンスなどの問題から、productionではbabel-nodeではなくフロントのコード同様トランスパイルしたものを使用することが推奨されています。

[babel/example-node-server](https://github.com/babel/example-node-server)

```:production用にトランスパイル
"build": "BABEL_ENV=server babel index.js --out-file bundle.js",
"start:production": "node bundle.js"
```

# Koa v2のインストール

```shell-session
$ npm i koa@2
```

v2ではクラスコンストラクタを返すようになったので対応します。

```js:before
import koa from 'koa';
const app = koa();
```

```js:after
import Koa from 'koa';
const app = new Koa();
```

# migration

## generator -> async/await or Promise

generator functionをasync/awaitに書き換えていきます。

```js:before
app.use(function *(next) {
  try {
    yield next;
  } catch (err) {
    if (401 == err.status) {
      this.status = 401;
      this.set('WWW-Authenticate', 'Basic');
      this.body = 'authorization required';
    } else {
      this.throw(err);
    }
  }
});
```

```js:async/await
app.use(async (ctx, next) => {
  try {
    await next;
  } catch (err) {
    if (401 == err.status) {
      ctx.status = 401;
      ctx.set('WWW-Authenticate', 'Basic');
      ctx.body = 'authorization required';
    } else {
      ctx.throw(err);
    }
  }
});
```

あるいはPromiseで書くこともできます。

```js:promise
app.use((ctx, next) => {
  next().catch((err) => {
    if (401 == err.status) {
      ctx.status = 401;
      ctx.set('WWW-Authenticate', 'Basic');
      ctx.body = 'authorization required';
    } else {
      ctx.throw(err);
    }
  });
});
```

## koa-convert

未対応のmiddlewareは、[koa-convert](https://github.com/koajs/convert)を使ってgenerator functionをPromiseを使用したものに変換することができます。

```js:before
import logger from 'koa-logger';
import serve from 'koa-static';

app.use(logger());
app.use(serve('.'));
```

```js:after
import logger from 'koa-logger';
import serve from 'koa-static';
import convert from 'koa-convert';

app.use(convert(logger()));
app.use(convert(serve('.')));
```

# さいごに

とりあえずconvertで囲ってしまえばどうにかなるのは楽でいいですね。

async/awaitのためだけにサーバーサイドでBabelを使用するのは、ビルドプロセスも複雑になるし正直あまり筋のいい方法とは言えないのでNode.js v7入れられるならそちらの方が良いでしょう。

ちなみにKoa v2の正式リリースはNode.js上でstableなasync/awaitが実装されたらとのことなので、Node.js v8が出る頃には出そうですね:smiley:

# FYI: フレームワークDL数比較

Koaよりhapi,restifyの方が多いようです。。。[^1] 

[^1]: Meteorはnpmで入らないためDL数判らず

|  | last month(2016/12/17) |
|:-:|--:|
|[express](https://www.npmjs.com/package/express)   |7,942,528   |
|[restify](https://www.npmjs.com/package/restify) | 258,329 |
|[hapi](https://www.npmjs.com/package/hapi)   |246,358    |
|[Koa](https://www.npmjs.com/package/koa)   |156,030   |
|[sails](https://www.npmjs.com/package/sails)   |67,201   |
|[Trails](https://www.npmjs.com/package/trails) |10,423 |

[Node.js Advent Calendar 2016](http://qiita.com/advent-calendar/2016/nodejs) 17日目の記事でした。