NestJS Interceptors
Interceptors are @Injectable() classes implementing NestInterceptor. Their
intercept(context: ExecutionContext, next: CallHandler) method runs logic
before the route handler and transforms the response stream returned by
next.handle() using RxJS operators. They are the idiomatic place for
cross-cutting concerns — response shaping, logging, timeouts, caching — that
should stay out of controllers and services.
When to Apply
Apply these rules when you are:
- Writing or reviewing a class that implements
NestInterceptor. - Wrapping handler responses in a consistent envelope.
- Adding request/response logging or latency measurement.
- Enforcing per-request timeouts.
- Caching GET responses with
CacheInterceptor. - Deciding whether to bind an interceptor globally, per-controller, or per-route.
Do not reach for an interceptor when the work is core business logic — that
belongs in a service (see interceptors-not-for-mutation-logic).
Rules
| Rule | Impact | Summary |
|---|---|---|
| response-transform-interceptor | HIGH | Wrap responses in a consistent envelope via map(). |
| logging-interceptor | MEDIUM | Measure handler duration with tap(); log method/url/time. |
| timeout-interceptor | HIGH | Fail slow requests with timeout() + RequestTimeoutException. |
| cache-interceptor | MEDIUM | Cache GET responses with CacheInterceptor + CacheModule. |
| interceptors-not-for-mutation-logic | HIGH | Keep business logic in services; interceptors are cross-cutting only. |
| bind-globally-vs-scoped | MEDIUM | Prefer APP_INTERCEPTOR for DI-friendly global binding. |
How to Use
- Identify the cross-cutting concern (transform, log, timeout, cache).
- Open the matching rule file and follow the Correct pattern.
- Keep
intercept()thin — pipe RxJS operators ontonext.handle(), never put domain logic inside. - Choose a binding scope with
bind-globally-vs-scoped: route, controller, or global viaAPP_INTERCEPTOR.