hakobera's blog

技術メモ。たまに雑談

Router-Line はじめました

4/18 に開催された 東京Node学園 5時限目 で @KOBA789 さんが自作のルータである Router-Line を紹介していました。

その時から便利そうとは思っていたのですが、最近ちょっとした API サーバを書く時に実際に Router-Line を使ってみて、改めて良いなと思ったので、サンプルを見せながら、個人的に便利だと思う使い方を紹介します。

Router-Line とは

KOBA789/router-line · GitHub

github で「A URL routing module for Node.js」と書いてある通り、Router-Line はシンプルな URL ルータで、それ以外の機能は持っていません。サーバを構築するには Node.js の http モジュールなどと組み合わせて使う必要があります。

個人的には API サーバのルーティングには、Express (Connect) を使うより、すっきりかけて、速度も速いので合っていると感じています。

致命的な欠点は、サンプルがないこと、ですかね、はい。ドキュメントがほぼ皆無で、最初どうやってサーバと組み合わせるか悩みました。自分はテストコード読んで理解しましたが、初心者にはお勧めできない、的な雰囲気になってしまっているのがとても勿体無い。(ので、この記事を書きました)

Router-Line 事始め

Router-Line を利用した API サーバの雛形を書くと以下のようになります。

var http = require('http'),
  url = require('url'),
  Router = require('router-line').Router;

var router = new Router();

function send(res, status, contentType, body) {
  res.writeHead(status, { 'Content-Type': contentType });
  res.end(body);
}

/*
 * ここからルーティングの設定
 */
router.add('GET', '/', function (req, res) {
  res.end('hello world');
});

// ここまでルーティングの設定

/*
 * HTTP サーバと連携させる。以下は個人的に使っているテンプレートコード。
 */
var app = http.createServer(function (req, res) {
  var uri, r, handler;

  uri = url.parse(req.url, true);
  r = router.route(req.method, uri.pathname);
  if (r) {
    req.param = function (key) {
      return r.params[key] ? r.params[key] : uri.query[key];
    }
    handler = r.value;

    if (typeof handler === 'function') {
      handler(req, res, r.params);
    } else if (typeof handler === 'string') {
      send(res, 200, 'text/plain', handler);
    } else {
      send(res, 200, 'application/json', JSON.stringify(handler));
    }
  } else {
    send(res, 404, 'application/json', JSON.stringify({ reason: 'This not the URL you are looking for' }));
  }
});
app.listen(process.env.PORT || 3000);
console.log('Server listen on port %d', app.address().port);

Router-Line の基本

Router の作成
Router = require('router-line').Router;
var router = new Router();

普通ですね。今回は1つしか使いませんが、何個でも作れます。

Routing Handler の設定
// HTTP メソッドを指定する add メソッド
router.add('HTTP メソッド名(GET|POST|PUT|DELETE)', 'マッチさせる URL', Routing Handler [function, string, object なんでも OK]);

// 全HTTP メソッドを一括指定できる ANY メソッド
router.ANY('マッチさせる URL', Routing Handler [function, string, object なんでも OK]);

基本的には add() しか使わない。というか、add() しか使ったことない。

router.add('GET', '/', function (req, res) {
  res.end('index');
});

これを取得するには Router.route() メソッドを使います。

var app = http.createServer(function (req, res) {
  var uri, r, handler;

  uri = url.parse(req.url, true);
  r = router.route(req.method, uri.pathname);

URLに一致する handler がない場合は undefined が返ります。
URLに一致する handler がみつかった場合、params, value というプロパティを持った Object が返ります。

{
  params: { Path parameter の解析結果 object },
  value: { add()/ANY() で設定した handler object }
}

以上です。簡単ですね。

応用編

Path Parameter を使う

コロン (:) で始まる文字列は Path Parameter として解析されます。例えば、以下の様な感じです。

/*
 * pathParams には route().params の結果が入っている。
 */
router.add('GET', '/users/:user/apps/:app/:id', function (req, res, pathParams) {
  send(res, 200, 'application/json', JSON.stringify(pathParams));
});

これに対して "/users/user1/apps/app2/3" でアクセスすると、

{"user":"user1","app":"app2","id":"3"}

が返ります。実際に動作するサンプルを heroku においておきましたので、色々と URL を変えて試してみてください。 サンプル

Path Parameter だけど、一部 Option パラメータを指定

URL を括弧で囲むとその部分は省略可能なパラメータとして解析されます。例えば以下。

router.add('GET', '/items/:item(/type/:type)', function (req, res, pathParams) {
  send(res, 200, 'application/json', JSON.stringify(pathParams));
});

これに対して "/items/item1" でアクセスすると、

{"item":"item1"}

こうなって、"/items/item1/type/small" でアクセスすると、

{"item":"item1","type":"small"}

となります

ソース置き場

今回使ったサンプルは以下に置いてあります。

router-line-exercise — Gist

まとめ

なんとなく Node.js なら Express 的な風潮が見受けられますが、API サーバ(画面なし、form から POST なし) を書くなら Router-Line を検討してみても良いと思います、というか Router-Line お勧めです。