Performance: Why interceptors add cross-cutting logic
Interceptors affect the request-response cycle timing and can impact interaction responsiveness by adding extra processing steps.
Use lightweight, asynchronous interceptors that delegate heavy work outside the request cycle or use caching.Use interceptors that perform heavy synchronous operations or blocking calls inside intercept method.| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Heavy synchronous interceptor logic | N/A (server-side) | N/A | N/A | [X] Bad |
| Lightweight asynchronous interceptor logic | N/A | N/A | N/A | [OK] Good |
Interceptors help to add logic that applies to many parts of the app without repeating code. Why is this useful?NestInterceptor interface and define the intercept() method.@Controller() and handle() is not an interceptor method; services need methods; plain functions lack interface.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before handler');
return next.handle().pipe(
tap(() => console.log('After handler'))
);
}
}next.handle() which runs the main handler.tap operator runs after the main handler completes, logging 'After handler'.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class SilentInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Intercepted');
next.handle();
}
}next.handle() to continue the request flow.next.handle() is called but its result is not returned, so the request does not proceed and logs may not appear.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
return next.handle().pipe(
tap(() => {
const elapsed = Date.now() - start;
console.log(`Request took ${elapsed}ms`);
})
);
}
}next.handle() and uses tap() to run code after the handler completes.tap() operator runs side effects after the observable emits, so it correctly logs elapsed time after the request finishes.Pass the test and this pattern moves unknown