ネストJSのガード、インターセプター、フィルターの詳細解説

NestJSはリクエストを管理・処理するための強力なツールと機能を提供します。この記事では、NestJSのガード(Guards)、インターセプター(Interceptors)、フィルター(Filters)について詳しく掘り下げ、実際の使用例を通じてそれらの使用シーンを説明します。また、これらとミドルウェアの違いについてもケーススタディを通じて説明します。

ガード(Guards)

ガードの概念

ガードは、リクエストがハンドラーに到達する前に検証やチェックを行うためのメカニズムです。通常、認証や認可ロジックの実装に使用されます。

ガードの作成

ガードを作成するには、CanActivateインターフェースを実装し、canActivateメソッドを定義します。以下は簡単な認証ガードの例です:

1
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
2
import { Observable } from "rxjs";
3
4
@Injectable()
5
export class AuthGuard implements CanActivate {
6
canActivate(
7
context: ExecutionContext,
8
): boolean | Promise<boolean> | Observable<boolean> {
9
const request = context.switchToHttp().getRequest();
10
return this.validateRequest(request);
11
}
12
13
validateRequest(request: any): boolean {
14
// 認証ロジック
15
return request.headers.authorization === "my-secret-token"; // 簡単な例
16
}
17
}

ガードの適用

ガードは、コントローラーまたはメソッドレベルで適用でき、@UseGuardsデコレーターを使用します:

コントローラーレベル
1
import { Controller, Get, UseGuards } from "@nestjs/common";
2
import { AuthGuard } from "./auth.guard";
3
4
@Controller("cats")
5
@UseGuards(AuthGuard)
6
export class CatsController {
7
@Get()
8
findAll() {
9
return "This action returns all cats";
10
}
11
}
メソッドレベル
1
import { Controller, Get, UseGuards } from "@nestjs/common";
2
import { AuthGuard } from "./auth.guard";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseGuards(AuthGuard)
8
findAll() {
9
return "This action returns all cats";
10
}
11
}
グローバルレベル
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { AuthGuard } from "./auth.guard";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalGuards(new AuthGuard());
8
await app.listen(3000);
9
}
10
bootstrap();

ガードの実際の使用シーン

ガードは、認証や認可が必要なルートを保護するために頻繁に使用されます。たとえば、ユーザーがログインしないとアクセスできないルートでは、ガードを使用してユーザーの認証を行います。

インターセプター(Interceptors)

インターセプターの概念

インターセプターは、リクエストの処理前後にカスタムロジックを実行するためのメカニズムです。ログ記録、レスポンスデータの変換、エラーハンドリングなどに使用されます。

インターセプターの作成

インターセプターを作成するには、NestInterceptorインターフェースを実装し、interceptメソッドを定義します。以下はレスポンスデータのフォーマットを行うインターセプターの例です:

1
import {
2
Injectable,
3
NestInterceptor,
4
ExecutionContext,
5
CallHandler,
6
} from "@nestjs/common";
7
import { Observable } from "rxjs";
8
import { map } from "rxjs/operators";
9
10
@Injectable()
11
export class TransformInterceptor implements NestInterceptor {
12
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
13
return next.handle().pipe(map((data) => ({ data })));
14
}
15
}

インターセプターの適用

インターセプターは、コントローラーまたはメソッドレベルで適用でき、@UseInterceptorsデコレーターを使用します:

コントローラーレベル
1
import { Controller, Get, UseInterceptors } from "@nestjs/common";
2
import { TransformInterceptor } from "./transform.interceptor";
3
4
@Controller("cats")
5
@UseInterceptors(TransformInterceptor)
6
export class CatsController {
7
@Get()
8
findAll() {
9
return [{ name: "Tom" }, { name: "Jerry" }];
10
}
11
}
メソッドレベル
1
import { Controller, Get, UseInterceptors } from "@nestjs/common";
2
import { TransformInterceptor } from "./transform.interceptor";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseInterceptors(TransformInterceptor)
8
findAll() {
9
return [{ name: "Tom" }, { name: "Jerry" }];
10
}
11
}
グローバルレベル
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { TransformInterceptor } from "./transform.interceptor";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalInterceptors(new TransformInterceptor());
8
await app.listen(3000);
9
}
10
bootstrap();

インターセプターの実際の使用シーン

インターセプターは、ログ記録、レスポンスデータの変換、エラーハンドリングに頻繁に使用されます。たとえば、レスポンスデータを一括してフォーマットする必要があるルートでは、インターセプターを使用してデータを変換します。

フィルター(Filters)

フィルターの概念

フィルターは、未処理の例外をキャッチして処理するためのメカニズムです。コントローラー内の任意の未処理の例外をキャッチし、カスタムのエラーハンドリングロジックを実行できます。

フィルターの作成

フィルターを作成するには、ExceptionFilterインターフェースを実装し、catchメソッドを定義します。以下はグローバル例外フィルターの例です:

1
import {
2
ExceptionFilter,
3
Catch,
4
ArgumentsHost,
5
HttpException,
6
} from "@nestjs/common";
7
import { Request, Response } from "express";
8
9
@Catch(HttpException)
10
export class HttpExceptionFilter implements ExceptionFilter {
11
catch(exception: HttpException, host: ArgumentsHost) {
12
const ctx = host.switchToHttp();
13
const response = ctx.getResponse<Response>();
14
const request = ctx.getRequest<Request>();
15
const status = exception.getStatus();
16
17
response.status(status).json({
18
statusCode: status,
19
timestamp: new Date().toISOString(),
20
path: request.url,
21
});
22
}
23
}

フィルターの適用

フィルターは、コントローラーまたはメソッドレベルで適用でき、@UseFiltersデコレーターを使用します。また、グローバルにも適用できます:

コントローラーレベル
1
import { Controller, Get, UseFilters } from "@nestjs/common";
2
import { HttpExceptionFilter } from "./http-exception.filter";
3
4
@Controller("cats")
5
@UseFilters(HttpExceptionFilter)
6
export class CatsController {
7
@Get()
8
findAll() {
9
throw new HttpException("Forbidden", 403);
10
}
11
}
メソッドレベル
1
import { Controller, Get, UseFilters } from "@nestjs/common";
2
import { HttpExceptionFilter } from "./http-exception.filter";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseFilters(HttpExceptionFilter)
8
findAll() {
9
throw new HttpException("Forbidden", 403);
10
}
11
}
グローバルレベル
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { HttpExceptionFilter } from "./http-exception.filter";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalFilters(new HttpExceptionFilter());
8
await app.listen(3000);
9
}
10
bootstrap();

フィルターの実際の使用シーン

フィルターは、グローバルエラーハンドリングに頻繁に使用されます。たとえば、未処理の例外を一括して処理するグローバル例外フィルターを作成し、統一されたエラーレスポンスを返すことができます。

ミドルウェア(Middleware)

ミドルウェアの概念

ミドルウェアは、リクエストがルートハンドラーに到達する前やレスポンスがクライアントに送信される前に実行される関数です。ミドルウェアは、リクエストの処理、レスポンスの変更、リクエスト-レスポンスサイクルの終了、または次のミドルウェア関数の呼び出しに使用できます。

ミドルウェアの作成

ミドルウェアを作成するには、NestMiddlewareインターフェースを実装し、useメソッドを定義します。以下は簡単なログ記録ミドルウェアの例です:

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class LoggerMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
console.log(`Request...`);
8
next();
9
}
10
}

ミドルウェアの適用

ミドルウェアは、モジュール内で適用でき、forRoutesメソッドを使用します:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { CatsController } from "./cats.controller";
3
import { LoggerMiddleware } from "./logger.middleware";
4
5
@Module({
6
controllers: [CatsController],
7
})
8
export class CatsModule implements NestModule {
9
configure(consumer: MiddlewareConsumer) {
10
consumer.apply(LoggerMiddleware).forRoutes(CatsController);
11
}
12
}

ミドルウェアの実際の使用シーン

ミドルウェアは、ログ記録、リクエストの検証、レスポンスの圧縮などに頻繁に使用されます。たとえば、すべてのリクエストをログ記録する必要があるモジュールでは、ミドルウェアを使用してリクエスト情報を記録します。

ガード、インターセプター、フィルター、ミドルウェアの違い

  • ガード(Guards):リクエストが継続できるかどうかを制御するために使用され、主に認証や認可に使用されます。
  • インターセプター(Interceptors):リクエスト前後に追加のロジックを実行するために使用され、ログ記録、レスポンスデータの変換、エラーハンドリングに使用されます。
  • フィルター(Filters):未処理の例外をキャッチして処理するために使用され、主にグローバルエラーハンドリングに使用されます。
  • ミドルウェア(Middleware):リクエストがルートハンドラーに到達する前やレスポンスがクライアントに送信される前に追加のロジックを実行するために使用され、ログ記録、リクエストの検証、レスポンスの圧縮に使用されます。

まとめ

本記事では、NestJSのガード、インターセプター、フィルター、ミドルウェアについて詳しく解説し、実際の使用シーンを例示しました。これらのツールを適切に使用することで、強力で柔軟なNestJSアプリケーションを構築することができます。また、これらのツールの違いについても説明し、それぞれの適用シーンを理解しやすくしました。