Angular interceptors tutorial
Angular Interceptors Explained: A Beginner’s Guide to Global HTTP Handling
The Problem with Repetitive HTTP Logic
Imagine you're creating an Angular app. Each HTTP request needs an authentication token, error handling, and logging. All the code is scattered across different services without any centralization. At this point of the game, you're smothered in byte blood—running 'head, tail, head, tail' all day long—covering your app with technical debt.
Introducing Angular Interceptors
Middleware that stands between your app and the server, interceptors allow you to modify requests, handle errors globally, and streamline tasks such as logging. This guide ensures that you replace this verbose mess of code with clear, reusable interceptors before the end of this book. Let's get started!
What Are Angular Interceptors?
Interceptors are classes that implement HttpInterceptor. They intercept HTTP requests or responses before the request is made to the server. Conversely, they intercept the response before it reaches your app.
Step 1: Creating Your First Interceptor
We'll make an interceptor which adds a token to the header of every HTTP request.
1a. Generate the Interceptor
ng generate interceptor auth
1b. Implement the Intercept Method
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Clone the request and add the auth header
const authToken = 'YOUR_TOKEN';
const authReq = req.clone({
setHeaders: { Authorization: `Bearer ${authToken}` }
});
return next.handle(authReq);
}
}
1c. Register the Interceptor
Add it to your app’s providers in app.module.ts:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]
})
export class AppModule {}
Now, every HTTP request from your app includes the token automatically!
Real-World Use Cases for Interceptors
1. Global Error Handling
If an HTTP request results in an error (e.g., 404, 500), intercept it and replace the message that's sent back to users with something user-friendly.
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError(error => {
console.error('Error:', error);
alert('Something went wrong!');
return throwError(() => error);
})
);
}
2. Logging HTTP Activity
To keep track of when you made requests and which URLs were addressed.
3. Modify Responses
Modify the data returned before loading it into a component.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
map(event => {
if (event instanceof HttpResponse) {
return event.clone({ body: event.body.data }); // Unwrap nested data
}
return event;
})
);
}
Interceptor Best Practices
- Clone Requests: Always clone the request before modifying it to prevent side effects.
- Order Matters: Requests are modified in the sequence they're passed to HTTP_INTERCEPTORS.
- Use multi: true: Allows multiple interceptors in your app at once.
- Avoid Memory Leaks: Use takeUntil to unsubscribe from consumer services.
Common Pitfalls and How to Avoid Them
- Don't make changes to the original request: Always use clone().
- Deal with errors properly: Always return throwError after intercepting.
- Keep Interceptors Focused: Separate interceptors for different functionalities (e.g., authentication, logging).