NestJs - Interceptor

choko's avatar
Jun 29, 2024
NestJs - Interceptor
  • request와 response 중간에서 값을 intercept 한 뒤 보내는 역할을 한다.
    • request → controller/service 등 내부 로직 → interceptor → response
    • 추가적인 로직이 필요한 경우 사용
      • ex) Logging, Response custom
  • Aop에서 영감을 받은 여러 기능을 제공한다.
    • 메서드 실행 전후에 추가 로직 바인딩
    • 함수에서 반환된 결과 변환
    • 함수에서 던져진 예외 변환
    • 기존 함수 동작 확장, 특정 조건에 따라 함수 재정의
  • @Injectable() 데코레이터가 있어야 하고, NestInterceptor 인터페이스를 반드시 implement 해야 한다.
    • intercept(ExecutionContext, CallHandler) 메서드를 구현해야 한다.
      • @Injectable() export class ResponseInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> { return next.handle().pipe( ... ) } }
      • ExecutionContext
      • CallHandler
        • handle() 메서드를 구현한다. handler()
      • interceptObservable 를 반환한다.
        • Observable 는 event stream이다.
       
  • loggin example
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`)), ); } }
 
 

Binding interceptor

  • 마찬가지로 global, class, method 단위로 scope 범위를 조절할 수 있다.
// controller 범위 @UseInterceptors(new LoggingInterceptor()) export class CatsController { ... } import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; // global 범위 @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }, ], }) export class AppModule {}
 
 

Response mapping

  • Observablemap() 을 사용하여 응답 객체를 새로 생성된 response 객체에 할당하고 반환하는 예시 코드
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response<T> { data: T; } @Injectable() export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> { intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> { return next.handle().pipe(map(data => ({ data }))); } }
 

Error mapping

  • ObservablecatchError() 로 에러 재정의도 가능하다
import { Injectable, NestInterceptor, ExecutionContext, BadGatewayException, CallHandler, } from '@nestjs/common'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() export class ErrorsInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next .handle() .pipe( catchError(err => throwError(() => new BadGatewayException())), ); } }
 

More operators

  • RxJS 연산자를 이용해 더 많은 기능을 제공할 수 있다.
  • timeout 예시 코드
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common'; import { Observable, throwError, TimeoutError } from 'rxjs'; import { catchError, timeout } from 'rxjs/operators'; @Injectable() export class TimeoutInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle().pipe( timeout(5000), catchError(err => { if (err instanceof TimeoutError) { return throwError(() => new RequestTimeoutException()); } return throwError(() => err); }), ); }; };
 
Share article

Tom의 TIL 정리방