Discover how one simple tool can save you from endless repetitive code and bugs!
Why interceptors add cross-cutting logic in NestJS - The Real Reasons
Imagine you have to add logging, error handling, and timing to every function in your NestJS app manually by writing the same code inside each controller or service method.
Manually adding these repeated tasks everywhere makes your code messy, hard to maintain, and easy to forget or make mistakes. It slows you down and causes bugs.
Interceptors let you write this shared logic once and apply it automatically across many parts of your app, keeping your code clean and consistent.
async getData() {
console.log('Start');
try {
const result = await this.service.fetch();
console.log('End');
return result;
} catch (e) {
console.error(e);
throw e;
}
}@UseInterceptors(LoggingInterceptor)
async getData() {
return this.service.fetch();
}You can add or change shared behaviors like logging, caching, or error handling in one place and have it affect your whole app automatically.
In a real app, you might want to log every request's duration to find slow endpoints without touching each controller method.
Manual repetition of shared logic is error-prone and hard to maintain.
Interceptors centralize cross-cutting concerns for cleaner code.
This makes your app easier to update and debug.
Practice
Interceptors help to add logic that applies to many parts of the app without repeating code. Why is this useful?Solution
Step 1: Understand the role of interceptors
Interceptors run code before and after the main handler, allowing shared tasks like logging or error handling.Step 2: Compare with other options
Controllers handle requests, databases store data, and UI components are unrelated to interceptors.Final Answer:
They add shared logic before and after main code, like logging or timing. -> Option AQuick Check:
Interceptors add shared logic = A [OK]
- Thinking interceptors handle HTTP requests directly
- Confusing interceptors with database or UI roles
- Assuming interceptors replace controllers
Solution
Step 1: Recall NestJS interceptor pattern
Interceptors must implement theNestInterceptorinterface and define theintercept()method.Step 2: Check other options
Controllers use@Controller()andhandle()is not an interceptor method; services need methods; plain functions lack interface.Final Answer:
Implement the NestInterceptor interface and define the intercept() method. -> Option AQuick Check:
Interceptor interface + intercept() = B [OK]
- Confusing interceptors with controllers or services
- Missing the intercept() method
- Not using the NestInterceptor 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'))
);
}
}What will be printed to the console when a request is handled?
Solution
Step 1: Analyze the intercept method flow
The interceptor logs 'Before handler' immediately, then callsnext.handle()which runs the main handler.Step 2: Understand the tap operator
Thetapoperator runs after the main handler completes, logging 'After handler'.Final Answer:
Before handler, then After handler -> Option BQuick Check:
Logs before and after handler = C [OK]
- Thinking only one log prints
- Confusing order of logs
- Ignoring the tap() operator effect
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();
}
}What is the problem?
Solution
Step 1: Check the intercept method return
The intercept method must return the observable fromnext.handle()to continue the request flow.Step 2: Identify missing return
Here,next.handle()is called but its result is not returned, so the request does not proceed and logs may not appear.Final Answer:
The intercept method does not return the observable from next.handle(). -> Option DQuick Check:
Missing return next.handle() = D [OK]
- Forgetting to return next.handle()
- Misplacing console.log after next.handle()
- Confusing decorators for interceptors
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`);
})
);
}
}Solution
Step 1: Understand timing measurement
The interceptor records start time before callingnext.handle()and usestap()to run code after the handler completes.Step 2: Confirm correct usage of RxJS tap()
Thetap()operator runs side effects after the observable emits, so it correctly logs elapsed time after the request finishes.Final Answer:
This interceptor correctly measures time before and after the handler using tap(). -> Option CQuick Check:
Timing with tap() after handler = A [OK]
- Measuring time only before handler
- Not returning next.handle() observable
- Using setTimeout instead of RxJS operators
Ready to prove it?
Pass the test and this pattern moves unknown
