NestJSのミドルウェアを深く理解する:リクエストとレスポンスを処理するミドルウェアの作成と適用

NestJSにおいて、ミドルウェア(Middleware)はリクエストとレスポンスを処理する重要なコンポーネントです。この記事では、NestJSのミドルウェアの概念について詳しく説明し、リクエストとレスポンスを処理するためのミドルウェアの作成と適用方法を解説します。

ミドルウェアとは

NestJSにおいて、ミドルウェアはリクエストオブジェクト(req)、レスポンスオブジェクト(res)、およびリクエスト-レスポンスサイクル内の次のミドルウェア関数(next)にアクセスできる関数です。ミドルウェアはリクエストを処理する前に操作を実行できます。例えば、ユーザー認証の検証、ログの記録、リクエストオブジェクトの修正などです。

ミドルウェアの定義と使用方法はExpress.jsと似ていますが、NestJSはより強力なモジュール化と型安全性を提供します。

ミドルウェアの作成

ミドルウェアを作成するには、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
}

上記の例では、LoggerMiddleware ミドルウェアが各リクエストを記録し、next() 関数を呼び出して次のミドルウェアまたはルートハンドラに制御を渡します。

ミドルウェアの適用

ミドルウェアを適用するには、モジュールの configure メソッドで MiddlewareConsumer を使用してミドルウェアの適用範囲を指定します。以下は LoggerMiddleware をすべてのルートに適用する例です:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { LoggerMiddleware } from "./logger.middleware";
3
4
@Module({
5
// その他のモジュール設定
6
})
7
export class AppModule implements NestModule {
8
configure(consumer: MiddlewareConsumer) {
9
consumer.apply(LoggerMiddleware).forRoutes("*");
10
}
11
}

apply メソッドで適用するミドルウェアを指定し、forRoutes メソッドでミドルウェアの適用範囲を指定します。forRoutes('*') はすべてのルートにミドルウェアを適用することを意味します。

ミドルウェアの例

リクエストオブジェクトの修正

以下のミドルウェアはカスタムプロパティ customProperty をリクエストオブジェクトに追加します:

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class CustomPropertyMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
req["customProperty"] = "Custom Value";
8
next();
9
}
10
}

レスポンスオブジェクトの修正

以下はレスポンスオブジェクトを修正するミドルウェアの例です:

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

複数のミドルウェアの協調

複数のミドルウェアを一緒に使用することができ、それぞれが独立して操作を実行します。以下は LoggerMiddlewareCustomPropertyMiddleware を使用する例です:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { LoggerMiddleware } from "./logger.middleware";
3
import { CustomPropertyMiddleware } from "./custom-property.middleware";
4
5
@Module({
6
// その他のモジュール設定
7
})
8
export class AppModule implements NestModule {
9
configure(consumer: MiddlewareConsumer) {
10
consumer.apply(LoggerMiddleware, CustomPropertyMiddleware).forRoutes("*");
11
}
12
}

上記の例では、LoggerMiddleware がリクエストのログを記録し、CustomPropertyMiddleware がリクエストオブジェクトにカスタムプロパティを追加します。すべてのリクエストがこれらの2つのミドルウェアを通過します。

forRoutesとexcludeの多種多様な使用方法

forRoutes メソッドは、ミドルウェアを特定のパス、コントローラー、またはメソッドに適用するために使用できます。

特定のパスに適用

1
consumer
2
.apply(LoggerMiddleware)
3
.forRoutes({ path: "cats", method: RequestMethod.GET });

特定のコントローラーに適用

1
import { CatsController } from "./cats.controller";
2
3
consumer.apply(LoggerMiddleware).forRoutes(CatsController);

複数のパスとメソッドに適用

1
consumer
2
.apply(LoggerMiddleware)
3
.forRoutes(
4
{ path: "cats", method: RequestMethod.GET },
5
{ path: "dogs", method: RequestMethod.POST },
6
);

特定のパスを除外

exclude メソッドは、特定のパスを除外してミドルウェアを適用しないようにするために使用されます:

1
consumer
2
.apply(LoggerMiddleware)
3
.exclude(
4
{ path: "cats", method: RequestMethod.GET },
5
{ path: "dogs", method: RequestMethod.POST },
6
)
7
.forRoutes("*");

上記の例では、LoggerMiddleware がすべてのパスに適用されますが、GET /catsPOST /dogs は除外されます。

関数型ミドルウェア

クラスミドルウェアに加えて、NestJSは関数を使用してミドルウェアを作成することもサポートしています。以下は関数型ミドルウェアの例です:

1
import { Request, Response, NextFunction } from "express";
2
3
export function logger(req: Request, res: Response, next: NextFunction) {
4
console.log(`Request...`);
5
next();
6
}

関数型ミドルウェアを使用する場合、NestMiddleware インターフェースを実装する必要はありません。直接モジュールで適用できます:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { logger } from "./logger.middleware";
3
4
@Module({
5
// その他のモジュール設定
6
})
7
export class AppModule implements NestModule {
8
configure(consumer: MiddlewareConsumer) {
9
consumer.apply(logger).forRoutes("*");
10
}
11
}

まとめ

この記事では、NestJSのミドルウェアの概念について詳しく説明し、リクエストとレスポンスを処理するためのミドルウェアの作成と適用方法を解説しました。ミドルウェアを使用することで、開発者はリクエスト処理の過程で様々な操作を実行でき、認証、ログ記録、リクエスト修正などを通じてアプリケーションの機能とセキュリティを強化できます。また、複数のミドルウェアの協調の例を示し、リクエストおよびレスポンスオブジェクトの修正方法、forRoutes および exclude メソッドを使用してミドルウェアの適用範囲を制御する方法、関数型ミドルウェアの作成と適用方法についても紹介しました。