はじめに
すべてのAPIは、自分では作成していないデータを受信します。ユーザーは不正なJSONを送信し、フィールドが欠落し、数値であるべき場所に文字列を送り、技術的には有効だが論理的に誤ったペイロードを送信します。検証層がなければ、そうした問題は静かにデータベースを破損させたり、意味不明な500エラーを引き起こしたりします。一貫したエラーハンドリング層がなければ、すべてのルートが異なる動作をします —プレーンな文字列を返すもの、HTMLエラーページを返すもの、何も返さないものもあります。
このレッスンでは両方の層を構築します:express-validatorライブラリを使用したリクエスト検証と、すべてのエラーレスポンスに同じ予測可能な形式を与える一元的なエラーハンドラーです。
受信リクエストデータの検証
express-validatorは、よくテストされたvalidator.jsライブラリをラップし、Expressミドルウェアに直接接続します。プロジェクトと一緒にインストールしてください:
検証ルールはミドルウェアの配列として定義され、ハンドラーが実行される前にルートにアタッチされます。以下は、有効なメールアドレスと8文字以上のパスワードを必須とするPOST /usersルートです:
validationResult(req)を呼び出すことで、ルールチェーンが見つけたすべての違反が収集されます。配列が空でない場合、リクエストはHTTP 422 Unprocessable Entityで即座に拒否されます — これはサーバーがリクエストを理解したが、データが無効であるため処理できないという、意味的に正しいステータスです。
ルート数が増えるにつれ、検証ルールを別のファイルに抽出して、ルートファイルをビジネスロジックのみに集中させることができます。
一元的なエラーハンドラーの構築
Expressにはエラーハンドリング専用のミドルウェアシグネチャがあります:3つではなく4つのパラメータです。next(err)を呼び出すか、asyncラッパー内でthrowするすべてのミドルウェアまたはルートは、通常のミドルウェアをスキップしてここに到達します。
専用ファイルを作成します:
メインアプリファイルで、すべてのルートの後に登録してください。順序が重要です — Expressはこのハンドラーが最後に定義されている場合にのみ呼び出します:
これで、すべてのルートがErrorオブジェクトにstatusCodeをアタッチしてnextに渡すことで問題を通知できます:
予期しないエラーを含め、すべてのエラーが同じハンドラーを通過し、同じJSON形式を返すようになりました。フロントエンドチームは各エンドポイントのレスポンスを推測する代わりに、単一のエラー解析関数を作成できます。
カスタムAppErrorクラスの作成
手動でアタッチしたstatusCodeプロパティを持つ生のErrorオブジェクトを渡すのは繰り返しが多くなります。小さなカスタムクラスでこれを整理できます:
isOperationalフラグにより、エラーハンドラーは意図的にスローされたエラーと予期しないランタイムクラッシュを区別して処理できます:
エラーハンドラー内でisOperationalをチェックすることで、完全なスタックトレースをログに記録するか、アラートを送信するか、単にメッセージをそのまま返すかを決定できます。
よくある間違い
エラーハンドラーをルートより前に配置することが最も多い間違いです —まだエラーが渡されていないため、トリガーされません。常に最後に登録してください。
すべてのクライアントエラーにHTTP 400を使用するのは不正確です。不正な構文には400、認証が欠落している場合は401、ユーザーは認証されているが許可されていない場合は403、リソースが存在しない場合は404、構造は有効だが内容がビジネスルールに違反する場合は422を使用してください。
本番環境でスタックトレースを公開することは実際のセキュリティリスクです。上記のerrorHandlerの例の条件付きスプレッド(process.env.NODE_ENV === 'development')により、スタックトレースはローカル環境にのみ表示されます。
非同期ルートハンドラーをtry/catchでラップするのを忘れると、未処理のプロミス拒否がエラーハンドラーを完全にバイパスします。薄いasyncラッパーユーティリティにより、すべての路由でボイラープレートなしにこのパターンを強制できます。
まとめ
express-validatorによる検証は、悪いデータを入り口で阻止し、クライアントに構造化されたフィードバックを返します。一貫したJSON形式を備えた一元的なエラーハンドラーは、APIを消費者にとって予測可能にし、成長に合わせて保守を容易にします。AppErrorクラスは両方の懸念を橋渡しします — コードベースのどこからでも予期された失敗を通知するクリーンな方法を提供し、単一のハンドラーが応答方法を決定できるようにします。
レッスンのチェックポイント
学んだ内容を復習し、回答へのフィードバックを受けましょう。