- 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()
intercept
는 Observable
를 반환한다.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
Observable
의map()
을 사용하여 응답 객체를 새로 생성된 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
Observable
의catchError()
로 에러 재정의도 가능하다
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