intermediate

NestJS interceptors

Apply interceptors for cross-cutting request/response behavior such as logging, mapping, timing, caching, and wrapping streams.

Interceptors wrap the handler execution pipeline after guards and before pipes/filters in the outbound direction. They implement `NestInterceptor` with `intercept(context, next)` and can transform results, add logging, or measure latency.

					@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const start = Date.now();
    return next.handle().pipe(
      tap(() => logger.log(`${Date.now() - start}ms`)),
    );
  }
}
				

Use interceptors for response mapping (wrap in `{ data }`), caching, timeout enforcement, and RxJS stream transforms. They run for both success and error paths when composed with operators.

On interviews: interceptors versus middleware (interceptors see the handler and can transform the return value), versus filters (filters handle exceptions), and when RxJS adds value.

Common pitfalls: business logic in interceptors, forgetting interceptors do not run if guards reject, and unbounded RxJS subscriptions.

The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.

Checklist:

  • Use interceptors for cross-cutting response behavior.
  • Prefer tap/map for logging and shaping.
  • Do not replace guards or pipes with interceptors.
  • Keep interceptors stateless when possible.