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.