Node.js
ExpressJS

ExpressJSでroutesをもっと分割する

More than 1 year has passed since last update.

ExpressJSではアプリケーションのルーティングの記述方法がいくつか用意されているようです。
通常は必要ないかもしれませんが、テスト用のルーティングでどうしても1ファイルのコード量が多くなってしまって、何とかもっと分割できないかと思い、試行錯誤して実現できたのでメモ。

参考: Routing

英語: http://expressjs.com/en/guide/routing.html
日本語: http://expressjs.com/ja/guide/routing.html

サンプルケース

例えば、下記のようなルート・パスがあるケースを考えます。

 GET: /sample/test-0/
 GET: /sample/test-0/foo
POST: /sample/test-0/
 GET: /sample/test-1/
 GET: /sample/test-1/bar
POST: /sample/test-1/
 GET: /sample/test-2/
 GET: /sample/test-2/hoge
POST: /sample/test-2/
 GET: /sample/test-3/
 GET: /sample/test-3/hage
POST: /sample/test-3/
 :

ここで、「test-[0-9]+」はIDとします。

本来やりたいルーティング設定

express.Routerを利用する方法で対応するのが一番スッキリしていると思うので下記のようなファイルを用意して対応したい。

routes/sample.js
var express = require('express');
var router = express.Router();

router.get('/:id/', function(req, res, next) {
    ...
});

module.exports = router;
app.js
var sample = require('./routes/sample');
...
app.use('/sample', sample);

要件1: テスト用にID固定にしたい

ID固定のルーティングを用意したい場合、下記のようにapp.jsを書き換えれば良い。

app.js
var sample = require('./routes/sample');
var test1 = require('./routes/test1');
var test2 = require('./routes/test2');
...
app.use('/sample/test-1', test1);
app.use('/sample/test-2', test2);
app.use('/sample', sample);

と言いたいところですが、もう1つ要件があってこれでは都合が悪い。

要件2: 一連のテストで複数のID固定ルート・パスを使いたい

簡単に言うと、1つのテストで「test-1」と「test-2」の組み合わせで動作するので、できれば両方のID固定ルート・パスを1ファイルに記述して管理したい。

多分ダメだと思ってやってみた方法

routes/test1_2.js
var express = require('express');
var router = express.Router();

router.get('/test-1/', function(req, res, next) {
    ...
});

router.get('/test-2/', function(req, res, next) {
    ...
});

module.exports = router;
app.js
var sample = require('./routes/sample');
var test1_2 = require('./routes/test1_2');
...
app.use('/sample', test1_2);
app.use('/sample', sample);

予想通り、最後にexpress#useに渡したものだけになりました。

解決方法

ようやく解決方法です。

一言で言うと、express.Routerを使いまわします。

routes/sample/common.js
var express = require('express');
var router = express.Router();

// 共通のルート・パス定義
router.get('/', function(req, res, next) {
    ...
});

module.exports = router;
routes/sample/test1_2.js
//var express = require('express');
//var router = express.Router();
var router = require('./common');

router.get('/test-1/', function(req, res, next) {
    ...
});

router.get('/test-2/', function(req, res, next) {
    ...
});

module.exports = router;
routes/sample/sample.js
//var express = require('express');
//var router = express.Router();
var router = require('./test1_2');

router.get('/:id/', function(req, res, next) {
    ...
});

module.exports = router;
routes/sample/index.js
module.exports = require('./sample');

express.Routerを使いまわす。

ポイント
//var express = require('express');
//var router = express.Router();
var router = require('./test1_2');

使い回す事になっているかどうかは定かではありませんが、ルータも結局モジュールなので、ファイル毎にexpress.Router()を呼ばずに、requireで他のルータモジュールを流用できるのでは?と思ってやってみたところ、一応期待通りに動作しました。

これは(変な)ID固定ルートが不要になった時に一番上位?のcommon.jsをrequireで読み込んであげれば良いので、最後までapp.jsを変更しなくても大丈夫です。

こんな特殊なケースがあるのかどうかわかりませんが、参考までに。