NestJSにおけるインターセプタに関するお勉強記録です。主に以下のページを見て学んだ内容になります。
Interceptors | NestJS - A progressive Node.js framework
インターセプタ
LaravelだとLaravel/Aspectというアスペクト志向プログラミングを実現できるパッケージに相当する機能のようです。コントローラのメソッドの前後に任意の処理を追加したり、メソッドの結果や例外を書き換えたりできます。
インターセプタの作成
NestJSでは以下のコマンドでインターセプタを作成できます。
nest g interceptor common/interceptor/logging
作成した直後はこんな感じです。NestInterceptorインターフェースのintercept()メソッドを実装しています。その中ではintercept()メソッドの第2引数のCallHandlerインターフェースのインスタンスnextでhandle()メソッドを呼び出しています。これがクライアントなどから呼び出されたコントローラのメソッドで、これの前後に好きなコードを書き加える事が出来ます。
1 2 3 4 5 6 7 8 9 |
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle(); } } |
以下の様に書き換えます。handle()メソッドはObservableを返すので、RxJSの演算子を使って様々な処理を実施することが可能です。以下の例では呼び出したいメソッドの前後でconsole.log()が実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { console.log('Before...'); const now = Date.now(); return next .handle() .pipe( tap(() => console.log(`After... ${Date.now() - now}ms`)), ); } } |
インターセプタの割り当て
他の機能と同じようにデコレータでクラスやメソッドに割り当てます。インターセプタの場合は@UseInterceptorsを使います。1行目の様に@nest/commonからUseInterceptorsをインポートし、12行目の様にクラスに割り当てます。メソッドへの割り当ても出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import { Controller, Get, Post, Param, Body, UseFilters, HttpStatus, HttpException, UseGuards, UseInterceptors } from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; import { HttpExceptionFilter } from 'src/common/filter/http-exception.filter'; import { SamplePipe } from 'src/common/pipe/sample.pipe'; import { AuthGuard } from 'src/common/guard/auth.guard'; import { LoggingInterceptor } from 'src/common/interceptor/logging.interceptor'; @Controller('cats') @UseGuards(AuthGuard) @UseInterceptors(LoggingInterceptor) export class CatsController { constructor(private catsService: CatsService) { } @Post() @UseFilters(new HttpExceptionFilter()) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll(); } @Get(':id') async findOne(@Param('id', SamplePipe) id: number) { return this.catsService.findOne(id); } } |
他にも色々な例が記載されていましたがRxJSの使い方の説明に近いので割愛します。一応今回のサンプルコードを置いておきます。