11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

こんにちは皆さん
今回は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では、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は便利そうですが、たくさんのパッケージを入れまくるので、ちょっと重そうです。
ここはkoa-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テンプレートまとめに、いい感じのテンプレートがそろっていますので、ここから引っ張ってきましょう。

ところが、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リクエストに対するミドルウェアの層構造について見たことが有りますが、今回の静的ファイルを取得する処理は、まさにこの仕組みを活用しているのです。

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

  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の検証を通じてミドルウェアの概念を理解する
Home · koajs/koa Wiki
商用利用できる無料のCSSテンプレートまとめ

11
13
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?