Menu

Expressのルーティングとexpress.Router()の使い方

Expressのルートは、メソッド、パス、ハンドラーの組み合わせです(app.get('/users/:id', handler))。関連するルートは express.Router() にまとめ、app.use('/api', router) でプレフィックスの下にマウントします。

このページのコードはエディタで実行できます。編集してすぐに結果を確認できます。

Expressのルーティングは、HTTPメソッドとパスをハンドラー関数に結びつけます。app.get('/users', handler) は /users へのGETリクエストに答え、app.post()、app.put()、app.delete() はほかのメソッドで同じことをします。/users/:id のようにコロンで始まるセグメントはルートパラメーターで、その値は req.params に入ります。アプリが大きくなっても整理しておけるように、関連するルートは express.Router() にまとめ、app.use('/api', router) でプレフィックスの下にマウントします。

GET /users/2 は {"id":2,"name":"Linus"} を返します。ルートパラメーターは常に文字列なので、ハンドラーは Number(req.params.id) で比較しています。ボディを {"name": "Grace"} にして POST /users を送ると 201 が返ります。続けて DELETE /users/1 と GET /users を送ると、リストが変わるのがわかります。

プレフィックス付きのexpress.Router()

ルーターは、自分のルートとミドルウェアを持つ小さなアプリです。パスは相対的で、/api/books にマウントしたルーターの中で /:id と書いたルートは /api/books/:id に答えます。実際のプロジェクトでは、ルーターはそれぞれ自分のファイルに置き、最後に module.exports = router と書きます。

GET /api/books/1 は {"id":1,"title":"Dune"} を返します。router.route('/:id') はパスを一度だけ書き、メソッドごとのハンドラーをチェーンでつなぎます。PUT /api/books/1 を送ると {"updated":"1"} が返ります。マウントするパスを /v2/books に変えると、同じルーターがそこで答えるようになります。

ルートの順番:最初にマッチしたものが勝つ

Expressは登録した順にルートを試し、最初にレスポンスを送ったところで止まります。パラメーターのルートはその位置に来るものを何でも受け取るので、その後に登録した固定のパスには決して届きません。

GET /users/new は {"route":"/users/:id","id":"new"} を返します。パラメーターのルートが先に登録されていて、それを受け取ってしまったからです。/users/new のルートを /users/:id より上に移して実行し直すと、{"route":"/users/new"} が返ります。続けて GET /books を送ると、一番下の404ハンドラーに届きます。

404ハンドラー

Expressはマッチしないリクエストに、Cannot GET /books という独自のHTMLページで答えます。APIならたいていJSONを返したいので、上のブロックのように、パスなしの app.use() を最後のルートとして追加します。これは、それより前のすべてのルートがリクエストを次に回したときだけ実行されます。ハンドラーが投げたエラーは別の話で、引数が4つの専用ハンドラーを使います。Expressのエラー処理で扱っています。

Express 5のルートパス

Express 5ではパスの書き方が変わりましたが、ネット上のコード例の多くはExpress 4向けに書かれています。

パターンExpress 4Express 5
すべてのパスapp.get('*', fn)app.get('/*splat', fn)
省略可能なパラメーター/users/:id?/users{/:id}
パラメーターのパターン/users/:id(\\d+)非対応。ハンドラーで値を確認する

* だけ、あるいはパラメーターの後の ? は、ルートを登録した時点で TypeError を投げるので、アプリが起動しません。

よくある間違い

404ハンドラーを最初に登録する。 パスなしの app.use() はすべてにマッチするので、どのリクエストも404になります。一番下に置きましょう。

プレフィックスが取り除かれることを忘れる。 /api にマウントしたルーターの中では、req.path に /api は含まれません。フルパスが必要なら req.originalUrl を使います。

パラメーターを数値と比較する。 req.params.id は文字列の '1' なので、req.params.id === 1 は常にfalseです。

よくある質問

app.useとapp.getの違いは何ですか?

app.get('/users', fn) は、パスがちょうど /users のGETリクエストのときだけ fn を実行します。app.use('/users', fn) はすべてのメソッドで、/users で始まるどのパスでも実行されます。だからルーターやミドルウェアのマウントに使われます。

Expressのルートで大文字と小文字を区別するには?

ルートはデフォルトで大文字と小文字を区別しないので、/Users は /users にマッチします。app.set('case sensitive routing', true) でオフにするか、ルーター単位で express.Router({ caseSensitive: true }) を使います。

ネストしたルーターでreq.paramsが空になるのはなぜですか?

ルーターが見えるのは自分のパスのパラメーターだけです。/users/:userId/posts にマウントするなら、express.Router({ mergeParams: true }) で作れば、親パスの req.params.userId が使えるようになります。

すべてのルートを1つのファイルに書くべきですか?

ルートがいくつもあるなら、やめましょう。リソースごとにルーターを作り(routes/users.js、routes/orders.js)、module.exports = router でエクスポートして、メインのファイルで app.use() を使ってそれぞれマウントします。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める