Interceptors
Interceptors sit between your component and the server, letting you modify requests before they go out and responses before they reach your code.
An interceptor sits BETWEEN your HTTP call and the server. It can:
- Modify the request BEFORE it goes out (add auth token, add base URL, log)
- Modify the response BEFORE it reaches your component (handle errors, transform data)
- Act on every HTTP request automatically โ no changes needed in individual services
"Interceptor = post office โ tum letter likho, post office stamp laga ke, address check karke bhejta hai."
Common use cases:
- Add Authorization token to every request
- Add base URL prefix
- Log all HTTP requests and responses
- Handle 401 errors globally โ redirect to login
- Show/hide loading spinner on request start/end
- Add request timing for performance monitoring
Interceptors work like middleware in Express.js or Django โ every request passes through them like a pipeline.
Functional interceptors are the new way (Angular 15+). They are simple functions instead of classes.
// auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const token = authService.getToken();
// Clone the request and add the auth header
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
// Pass the cloned request to the next handler
return next(authReq);
};
"Functional = lambda function โ simple, clean, no class."
Registration:
// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor])
),
]
};
req.clone() is important โ you NEVER mutate the original request. You clone it and modify the clone. This prevents bugs in other interceptors.
Here are the most common interceptor use cases with real code:
1. Auth โ Add Bearer Token
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
return next(req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
}));
};
2. Logging โ Log Every Request
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const start = performance.now();
console.log(`[HTTP] ${req.method} ${req.url}`);
return next(req).pipe(
tap(() => console.log(`[HTTP] ${req.url} took ${performance.now() - start}ms`))
);
};
3. Base URL โ Prepend API URL
export const baseUrlInterceptor: HttpInterceptorFn = (req, next) => {
if (!req.url.startsWith('http')) {
return next(req.clone({
url: `https://api.hyderabad.com${req.url}`
}));
}
return next(req);
};
4. Error Handling โ Catch 401 Globally
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError(error => {
if (error.status === 401) {
inject(Router).navigate(['/login']);
}
return throwError(() => error);
})
);
};
"Interceptors = middleware โ har request ke saath automatically kaam karo."
You can chain multiple interceptors โ each one runs in sequence.
// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { loggingInterceptor } from './interceptors/logging.interceptor';
import { authInterceptor } from './interceptors/auth.interceptor';
import { errorInterceptor } from './interceptors/error.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
loggingInterceptor, // 1st โ logs request
authInterceptor, // 2nd โ adds token
errorInterceptor // 3rd โ handles errors
])
),
]
};
Order matters! Here's how the pipeline works:
// REQUEST direction (first in array = first to run):
logging โ auth โ error โ (server)
// RESPONSE direction (LAST in array = first to run):
error โ auth โ logging โ (component)
"Pipeline = factory assembly line โ ek ke baad ek."
For requests: the first interceptor in the array runs first. For responses: the last interceptor in the array runs first (because of how RxJS pipes work โ the response flows backwards through the pipeline).
Interceptors can also modify responses using RxJS operators in the pipe().
export const responseInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
// Log the response
tap(event => {
if (event.type === HttpEventType.Response) {
console.log('Response received:', event.status);
}
}),
// Handle errors
catchError(error => {
if (error.status === 401) {
// Token expired โ redirect to login
inject(Router).navigate(['/login']);
}
if (error.status === 0) {
// Network error
console.error('Network error โ check your connection');
}
// Re-throw the error so component still gets it
return throwError(() => error);
})
);
};
"Request modify karo ya response modify karo โ dono possible."
tap()โ side effect (logging, analytics) without changing the valuecatchError()โ handle errors globallymap()โ transform the response dataretry()โ retry failed requests
You can use ANY RxJS operator inside an interceptor's pipe.
Key Takeaways
- โ Interceptors sit between component and server โ modify requests/responses globally
- โ Functional interceptors (HttpInterceptorFn) are the new way โ simple functions, no classes
- โ Always clone with req.clone() โ never mutate the original request object
- โ Chaining: first in array runs first for request, last runs first for response
- โ Common use cases: auth token, logging, base URL, error handling, loading spinner
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login