← 解説「インターセプター — 処理の前後に割り込む」に戻る
サンプルコードで身につける: インターセプター — 処理の前後に割り込む
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1前後にログを出す最小例
インターセプターの骨格だけを示す最小例です。next.handle() の呼び出しがハンドラ本体にあたり、その前に書いたコードが前処理、pipe(tap(...)) が後処理になる、という3点の位置関係を掴みます。
import {
CallHandler, ExecutionContext, Injectable, NestInterceptor,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable()
export class SimpleLogInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<unknown> {
console.log("① ハンドラ実行前");
return next.handle().pipe( // ② ハンドラ本体
tap(() => console.log("③ ハンドラ実行後")),
);
}
}
2レスポンスを共通の形に包む
ハンドラの戻り値を { data: ... } という共通フォーマットに包む例です。tap が「覗くだけ」なのに対し map は「戻り値を作り替える」オペレータで、全 API のレスポンス形式統一という実務頻出の要求に応えます。
import {
CallHandler, ExecutionContext, Injectable, NestInterceptor,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
@Injectable()
export class WrapResponseInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<unknown> {
// ハンドラの戻り値 result が { data: result } に変わる
return next.handle().pipe(map((result) => ({ data: result })));
}
}
// tap = 覗くだけ / map = 作り替える、という使い分け
3ハンドラを包む関数として再現
インターセプターの本質である「ハンドラを包んで前後に割り込む」構造を、Observable を使わないプレーン TypeScript で再現した例です。計測と整形をそれぞれ独立した包み紙として重ねられることが分かります。
TypeScript
4適用範囲の使い分け
インターセプターもガードと同じくハンドラ単位・コントローラ単位・アプリ全体の3段階で適用できます。計測やレスポンス統一のような横断的な関心事はグローバル適用にするのが実務の定番です。
import { Controller, Get, UseInterceptors } from "@nestjs/common";
import { LoggingInterceptor } from "./logging.interceptor";
// ① ハンドラ単位: このルートだけ計測したい場合
@Controller("orders")
export class OrdersController {
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {
return [];
}
}
// ② コントローラ単位: クラスの上に @UseInterceptors(...)
// ③ アプリ全体(main.ts):
// app.useGlobalInterceptors(new LoggingInterceptor());
// 計測・レスポンス統一は③のグローバル適用が実務の定番
5遅いAPIを検知して警告する
処理時間を計測し、1秒を超えた API だけ警告ログを残す実務インターセプターです。全エンドポイントに一律で仕掛けておくことで、性能劣化をユーザーからの苦情より先にログで察知できるようになります。
import {
CallHandler, ExecutionContext, Injectable, NestInterceptor,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable()
export class SlowApiInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<unknown> {
const started = Date.now();
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
tap(() => {
const elapsed = Date.now() - started;
if (elapsed > 1000) { // 1秒超の API を警告として記録
console.warn("SLOW: " + request.url + " " + elapsed + "ms");
}
}),
);
}
}