@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {
const req = ctx.switchToHttp().getRequest();
const start = Date.now();
return next.handle().pipe(
tap(() => console.log(req.method, req.url, Date.now() - start, "ms")),
);
}
}
An interceptor wraps the route handler, so it can run code before and after it. It implements NestInterceptor. Its intercept() method gets two things: the ExecutionContext (details about the current request and handler) and a CallHandler (usually named next).
How next.handle() works:
- Code before
next.handle()runs before the route handler. next.handle()returns an RxJSObservable. This is a stream that will carry the handler's result.- The handler only runs when someone subscribes to that observable. Nest subscribes to whatever your
intercept()returns.
So you add behaviour by adding RxJS operators to that stream:
tapfor side effects, like timing logsmapto change the responsecatchErrorto handle errorstimeoutfor time limits
For the timing logger, save the start time before calling next.handle(). Then use tap to log the elapsed time when the response value comes through.
If you return a stream without calling next.handle(), the handler never runs. Caching interceptors use this to skip the handler and return a cached value.
- An interceptor implements
NestInterceptor.intercept(context, next). - Code before
next.handle()runs before the handler. next.handle()returns an Observable of the handler's result.- RxJS operators such as
tap,mapandcatchErrorrun the after-logic. - If you don't call
next.handle(), the handler is skipped.
Read more
- Interceptors (https://docs.nestjs.com/interceptors)docs.nestjs.com