In modern web applications, managing HTTP requests and responses efficiently is crucial. Whether you're dealing with authentication, error handling, logging, or even showing loading indicators, it can quickly get overwhelming. That's where Angular Interceptors come into play.
At its core, an interceptor in Angular is a way to intercept and modify HTTP requests and responses before they are sent to the server or when the response is received in your application. Think of it like a middleman that sits between your application and the HTTP requests your app makes.
Interceptors provide a centralized, reusable way to handle common tasks such as:
In short, interceptors let you implement cross-cutting concerns in a clean and maintainable way, instead of duplicating logic in multiple places.
In Angular, interceptors are implemented using the Http Interceptor interface, which has a single method: intercept
. The intercept
method allows you to take control over the HTTP request and response flow. Here's the basic structure of an interceptor:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class MyInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Modify the request before sending it
const clonedRequest = req.clone({
setHeaders: { Authorization: 'Bearer token' }
});
// Pass the modified request to the next handler in the pipeline
return next.handle(clonedRequest).pipe(
catchError((error) => {
console.error('HTTP request failed', error);
throw error; // Re-throw or handle the error
})
);
}
}
req: HttpRequest<any>
: This is the original HTTP request. It contains all the details like URL, headers, body, and so on.next: HttpHandler
: This is the next step in the HTTP pipeline. Once you've modified the request (if necessary), you call next.handle()
to forward it.clone()
: The request is immutable in Angular. If you want to modify it (for example, by adding headers), you use req.clone()
to create a modified copy.catchError
: This operator is used to catch any errors in the HTTP response and allows you to handle them globally.Now that we know how interceptors work, let's explore the common use cases for interceptors in Angular. These use cases cover a wide range of situations that we often encounter in real-world apps.
One of the most common use cases for interceptors is to add an authentication token (such as a JWT) to the HTTP request headers. This allows your app to securely communicate with the backend, ensuring that each request is authorized.
Imagine you're building an app that requires users to log in, and you need to attach a JWT token to every request. Instead of manually adding the token to each HTTP request, you can centralize the logic in an interceptor.
Example:
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
const clonedRequest = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next.handle(clonedRequest);
}
}
Interceptors are also handy when you want to handle errors globally. For example, if the backend returns a 401 Unauthorized error (perhaps the token has expired), you can catch this error in your interceptor and perform actions such as logging the user out and redirecting them to the login page.
Example:
export class ErrorInterceptor implements HttpInterceptor {
constructor(private authService: AuthService, private router: Router) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error) => {
if (error.status === 401) {
// Handle unauthorized error
this.authService.logout();
this.router.navigate(['/login']);
}
return throwError(error);
})
);
}
}
In some scenarios, you may need to transform the request before it’s sent to the server or the response once it’s received. For example, you might want to automatically format dates or convert all strings to uppercase.
Example:
export class DateTransformInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.body && req.body.date) {
// Convert date to a specific format
req = req.clone({
body: { ...req.body, date: formatDate(req.body.date) }
});
}
return next.handle(req);
}
}
You might want to add global headers to every HTTP request, such as setting the Content-Type
or Accept
headers for all outgoing requests. Instead of manually adding headers to every request, you can use an interceptor to apply these headers globally.
Example:
export class HeaderInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const clonedRequest = req.clone({
setHeaders: { 'Content-Type': 'application/json' }
});
return next.handle(clonedRequest);
}
}
A very user-friendly use case for interceptors is showing a loading spinner while the HTTP request is in progress. Once the response is received (or the request fails), you can hide the spinner.
Example:
export class LoadingInterceptor implements HttpInterceptor {
constructor(private loadingService: LoadingService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.loadingService.show();
return next.handle(req).pipe(
finalize(() => {
this.loadingService.hide();
})
);
}
}
To use an interceptor in your Angular application, you need to register it in the AppModule
. Angular provides a special injection token called HTTP_INTERCEPTORS that allows you to provide one or more interceptors.
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
import { ErrorInterceptor } from './error.interceptor';
@NgModule({
imports: [HttpClientModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true // Allows multiple interceptors
},
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
}
]
})
export class AppModule {}
The multi: true
flag ensures that multiple interceptors can be used together without overriding each other.
Angular interceptors are a powerful tool that can help you manage HTTP requests and responses in a clean and centralized manner. They provide an elegant way to handle common tasks such as authentication, error handling, data transformation, and even global features like loading indicators and headers.
By using interceptors, you can keep your application logic clean and avoid repetitive code in individual components or services. They allow you to focus on writing business logic instead of managing HTTP concerns across your app.
What is the purpose of interceptor in Angular?
In Angular, interceptors serve as a powerful way to manipulate HTTP requests and responses. They can be used for various tasks such as:
Interceptors are applied globally and allow developers to modify the behavior of HTTP calls in a centralized manner.
Can we use multiple interceptors in Angular?
Yes, you can use multiple interceptors in Angular. Interceptors can inspect and modify both HTTP requests sent from your application and responses received from the server. When multiple interceptors are used, they form a chain where each interceptor can process the request before it is sent and the response after it is received. This allows for flexible and modular handling of common tasks such as authentication, error handling, logging, and more, by applying different logic in each interceptor along the request/response flow.
What is an interceptor in Angular, and how is it used?
In Angular, an interceptor acts as middleware that intercepts and modifies HTTP requests and responses. It allows you to perform actions before the request is sent to the server or after the response is received. Interceptors are useful for tasks such as adding authentication tokens, handling errors globally, logging, or transforming responses before they reach the application. They work on both the request and response level, providing a centralized way to manage HTTP interactions.
What are Angular interceptors for error handling?
Angular interceptors are essential tools for handling HTTP requests and responses globally, offering greater control over communication with the backend. For error handling, interceptors enable you to catch errors in one centralized location, eliminating the need to manage errors individually in every component or service. They allow you to modify error responses, convert server error messages into user-friendly formats, log errors automatically, and even retry failed requests using strategies like exponential backoff. By managing errors in a consistent and streamlined way, interceptors make applications more resilient and easier to maintain.