Dev Study
← 解説「例外フィルタ — エラーレスポンスの統一」に戻る

サンプルコードで身につける: 例外フィルタ — エラーレスポンスの統一

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1組み込み例外を投げる

フィルタを書く前の前提となる、NestJS 組み込み例外の使い方です。NotFoundException を投げるだけで 404 と標準形式の JSON が返るため、エラー処理の第一歩は「適切な例外を選んで投げる」ことになります。

import {
  Controller, Get, NotFoundException, Param,
} from "@nestjs/common";

@Controller("users")
export class UsersController {
  private users = [{ id: "1", name: "Alice" }];

  @Get(":id")
  findOne(@Param("id") id: string) {
    const found = this.users.find((u) => u.id === id);
    if (!found) {
      // 投げるだけで 404 + 標準形式の JSON が返る
      throw new NotFoundException("ユーザー " + id + " は存在しません");
    }
    return found;
  }
}
// 他にも BadRequestException(400) など、コード別に一通り揃っている

2発生URLを含める例外フィルタ

標準のエラー形式に「どの URL で起きたか」を加えるフィルタです。catch() の中でリクエストとレスポンスの両方を取り出せるため、エラーレスポンスに調査用の文脈情報を足すのが最初の実用的なカスタマイズになります。

import {
  ArgumentsHost, Catch, ExceptionFilter, HttpException,
} from "@nestjs/common";

@Catch(HttpException)
export class AppExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const statusCode = exception.getStatus();

    response.status(statusCode).json({
      statusCode,
      path: request.url, // どの URL で起きたかを必ず含める
      message: exception.message,
    });
  }
}

3例外→レスポンス変換を再現

例外フィルタの本質である「例外の種類を見てエラーレスポンスへ変換する」処理を、プレーン TypeScript の try/catch で再現した例です。想定外のエラーを 500 の汎用メッセージに置き換える定石も含んでいます。

TypeScript

4適用範囲の使い分け

例外フィルタもハンドラ単位・コントローラ単位・アプリ全体の3段階で適用できます。エラー形式の統一が目的である以上、実務ではほぼグローバル適用一択になる、という判断基準まで含めて押さえます。

import { Controller, Get, UseFilters } from "@nestjs/common";
import { HttpExceptionFilter } from "./http-exception.filter";

// ① ハンドラ単位: この API だけ特別なエラー形式にしたい場合
@Controller("orders")
export class OrdersController {
  @Get()
  @UseFilters(HttpExceptionFilter)
  findAll() {
    return [];
  }
}

// ② コントローラ単位: クラスの上に @UseFilters(...)
// ③ アプリ全体(main.ts):
//    app.useGlobalFilters(new HttpExceptionFilter());
// 「エラー形式の統一」が目的なので、実務ではほぼ③を選ぶ

5全例外キャッチと情報漏えい対策

引数なしの @Catch() であらゆる例外を受け止める、本番アプリの最後の砦となるフィルタです。想定外のエラーは詳細をログにだけ残し、クライアントには汎用メッセージを返すという情報漏えい対策の定石を実装しています。

import {
  ArgumentsHost, Catch, ExceptionFilter, HttpException,
} from "@nestjs/common";

@Catch() // 引数なしの @Catch() はあらゆる例外を捕まえる
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();

    if (exception instanceof HttpException) {
      const statusCode = exception.getStatus();
      response
        .status(statusCode)
        .json({ statusCode, message: exception.message });
      return;
    }
    // 想定外のエラー: 詳細はログにだけ残し、クライアントには隠す
    console.error("想定外のエラー:", exception);
    response
      .status(500)
      .json({ statusCode: 500, message: "Internal Server Error" });
  }
}