はじめに
API がわずかなエンドポイントを超えて成長すると、すべてを単一ファイルに詰め込むのはすぐに手に負えなくなります。Express には Router という組み込みツールがあり、リソースごとにルートを別々のモジュールに分割できます。これに controllers レイヤーを組み合わせれば、クリーンにスケールする構造が手に入ります。新しいチームメンバーは必要なものを見つけやすくなり、新規リソースの追加が既存コードに影響しません。
このレッスンでは、具体的なプロジェクトのレイアウトを順に確認します。routes は URL パターンと HTTP メソッドを定義し、controllers が実際のハンドラーロジックを保持し、app.js がそれらを結びつけます。この分離を早い段階で身につけることが、最も重要な習慣です。
プロジェクト構成
これから構築するディレクトリ構成は以下のとおりです。
パターンはシンプルです。リソースごとに1つのルートファイル、リソースごとに1つのコントローラーファイル。ルートは薄く保ち、各エンドポイントを処理するコントローラー関数を宣言するだけにします。コントローラーは専念し、自リソースのリクエスト/レスポンスロジックのみを含めるようにします。
Express Router
Express Router は独立してルートを処理するミニアプリケーションを作成し、それを app.js のパスにマウントします。
まずは users のルートファイルから始めましょう。
ルートでは `/users` や `/users/:id` ではなく、`/` や `/:id` のような相対パスを使用している点に注目してください。プレフィックスは app.js でルーターをマウントする際に追加されるため、同じパスを何度も書く必要はありません。
app.js でのルーターのマウント:
GET リクエスト `/users/42` は `router.get('/:id', ...)` に解決され、`req.params.id` は `'42'` になります。クリーンで予測可能です。
Controllers
Controllers は名前付き関数をエクスポートする通常のモジュールです。各関数は標準的な Express request handler で、`req` と `res`(必要に応じて `next`)を受け取り、レスポンスを送信する責務を持ちます。
ここで注目すべき点がいくつかあります。コントローラーは入力を検証し、問題があれば適切なステータスコードで早期リターンします。これにより正常系のコードが読みやすくなります。404チェックは明示的です。レコードが存在するかどうかは仮定しません。そして204 No Content(ボディなし)は、削除成功時の慣習的なレスポンスです。
よくあるミス
JSON リクエストボディのパースを忘れることが、初心者に最も多い問題です。app.js に `app.use(express.json())` がないと `req.body` は undefined になり、`createUser` や `updateUser` は何も受け取れないまま静かに動作してしまいます。
もう一つの落とし穴は、`req.params.id`(文字列)を数値の id と厳密等価で直接比較することです。`users.find(u => u.id === req.params.id)` のチェックは `'42' !== 42` のため常に失敗します。まず `parseInt` または `Number()` で変換してください。
最後に、レスポンスを送信した後に returnしないことで「Cannot set headers after they are sent」エラーが発生します。必ず `return res.status(404).json(...)` を使用してください。`return` があることで、Express は次の行へ進み2 回目のレスポンスを送信しようとしなくなります。
まとめ
Express Router を使うと、関連するルートを独自のモジュールにまとめ、app.js のプレフィックスにマウントできます。Controllers はハンドラーロジックをルートファイルから切り離し、各レイヤーの可読性・テスト容易性・拡張性を高めます。パターンは次のとおりです。ルートファイルは URL とメソッドを宣言し、コントローラー関数がロジックを処理してレスポンスを送信する。リソースごとに1 ファイルを守ることで、プロジェクトが成長してもこの構造は崩れずに保たれます。
レッスンのチェックポイント
学んだ内容を復習し、回答へのフィードバックを受けましょう。