Express.js

Express 5 の非同期エラーハンドリング完全ガイド

ⓘ本ページはプロモーションが含まれています

スポンサードリンク

1. Express 5 の概要とエラーハンドリングの基本

Express 5 は 非同期ハンドラ(async 関数)をデフォルトで受け入れrouter.use やルート定義において await が投げた例外を自動的に next(err) に変換します。これにより、従来必要だった try / catch → next(err) の記述が省略でき、コードがシンプルになる一方で エラーフローは Express 4 と同様のミドルウェアチェーン を通ります。

結論
非同期対応を活かしつつ、従来と同じ Error‑handling ミドルウェアを用意すれば、開発・本番共に安全なエラーハンドリングが実現できます。

1.1 非同期ハンドラで自動エラー捕捉される仕組み

Express 5 は内部で Promise の例外を捕捉し、next(err) を呼び出す処理を自動化しています。公式ドキュメントでも次のように説明されています([^1])。

1.2 従来の書き方との比較

項目 Express 4(手動) Express 5(自動)
async ハンドラ 必須だが try/catch が必須 例外は自動で次へ
コード量 catch → next(err) が必要 シンプルに記述可能
可読性 やや冗長 高可読性

2. Error‑handling ミドルウェアの設計指針

エラーハンドリングは 必ず 4 引数 (err, req, res, next) のシグネチャで実装します。Express は引数の個数でミドルウェア種別を判定し、4 番目がある場合にのみエラー用として呼び出す仕様です([^2])。

2.1 基本構造と必須要件

  • 4 引数で定義 → Express が自動的にマッチ
  • ステータスコードとメッセージは統一 → フロントエンドが期待する JSON 形式を保証

2.2 ステータスコード・メッセージの一本化

アプリ全体でエラー情報を統一すると保守性が大幅に向上します。以下はカスタムエラークラス AppError の例です。

活用例

  • next(err) だけで完結 → ルートハンドラはシンプルに保てる
  • エラー情報は statusCodepublicMessage に集約

3. 非同期ハンドラとバリデーションライブラリでの例外処理

非同期関数内で独自エラーを加工したり、express-validatorJoi のようなバリデーションツールと組み合わせる際のベストプラクティスをご紹介します。

3.1 try / catch と next(err) を併用する理由

自動捕捉は便利ですが、スタックトレースやエラーメッセージを加工したいケース では明示的に try / catch を書く方が安全です。

  • ロギングやエラー分類 が容易になる
  • 一貫したエラーフロー を維持できる

3.2 バリデーションエラーの統一フォーマット化

express-validatorJoi はそれぞれ異なる例外形式を返すため、共通の AppError に変換 してからミドルウェアへ渡します。

express‑validator の例

Joi の例

  • バリデーション失敗 → AppError に統一
  • 後続のエラーミドルウェアだけで一括処理可能

4. ロギングベストプラクティス & 本番向けレスポンス設計

エラー情報は 安全に記録し、環境ごとに出力を切り替える ことが重要です。以下では代表的なロガー(Winston・Pino)の設定例と、本番での JSON レスポンス方針を示します。

4.1 環境別ロガー設定

ロガー 開発時 本番
Winston カラフルなテキスト (simple) 構造化 JSON (json)
Pino pino-pretty で可読性向上 高速 JSON 出力

Winston 設定例

Pino 設定例

  • 環境変数 NODE_ENV のみで切り替え可能
  • 開発時はデバッグ情報を、運用時は最小限かつ構造化されたログを取得

4.2 本番向けエラーレスポンスの設計

本番環境では スタックトレースや内部実装情報は絶対に外部へ漏らさない ことがセキュリティ上必須です。代わりに、クライアントが期待する統一 JSON を返します。

  • 本番messagestatus のみ
  • 開発 → 追加で stack を付与し、デバッグを容易に

5. プロセスレベルの未捕捉例外対策(TypeScript 型安全)

サーバーが予期せぬ例外で停止しないよう、Node のプロセスイベントをハンドリングします。また、TypeScript を利用して エラーオブジェクトの型情報 を共有する方法をご紹介します。

5.1 uncaughtExceptionunhandledRejection のハンドラ

  • ロギング必須 → 原因特定が容易になる
  • プロセス終了 → メモリリークや不整合状態の防止

5.2 TypeScript でエラー型を統一

  • IDE 補完 が有効になり、プロパティのスペルミスを防止
  • アプリ全体で AppError をインポートすれば、一貫したエラー情報が保証されます

6. テストによる検証と実装導入手順

実装したエラーハンドリングが期待通りに動くかを 自動テスト で確認します。代表的なツールは SuperTestJest です。

6.1 SuperTest + Jest のユニットテスト例

  • ステータスコードと JSON メッセージ の統一が正しく機能しているかを検証
  • テストは CI パイプラインに組み込むことで、リファクタリング時の安全性が確保できます

6.2 サンプルリポジトリの取得とセットアップ手順

公式サンプルは GitHub に公開されています。以下のコマンドでプロジェクトをローカルにクローンし、依存パッケージをインストールしてください。

  • README に記載されたエンドポイントで動作確認が可能です
  • 本リポジトリは本記事のコードをそのまま利用できるよう構成されており、コピペミスの心配は不要です

まとめ

  1. Express 5 の非同期対応 によりハンドラはシンプルに書けるが、エラーハンドリングは従来と同様にミドルウェアチェーンで行う。
  2. 4 引数ミドルウェアAppError クラスでステータス・メッセージを統一すると保守性が向上する。
  3. バリデーションツールは 共通エラー (AppError) に変換 してから次のミドルウェアへ渡す。
  4. ロギングは環境別に設定し、本番ではスタックトレースを隠蔽した JSON を返す。
  5. プロセスレベルで 未捕捉例外をログ化・再起動 させ、TypeScript で型安全を担保する。
  6. SuperTest + Jest によるテストで 実装の正確性と回帰防止 を確認し、公式サンプルリポジトリで即座に導入できる。

これらのベストプラクティスを組み込めば、Express 5 アプリケーションは 堅牢かつメンテナンスしやすいエラーハンドリング基盤 を手に入れることができます。


[^1]: Express 5 Migration Guide – https://expressjs.com/en/guide/migrating-4-to-5.html
[^2]: Express Error Handling – https://expressjs.com/en/guide/error-handling.html

スポンサードリンク

-Express.js