I have been working in Angular for quite some time now and have worked on almost every version of it. It’s amazing how every version comes with new features. One feature that caught my eye is HttpInterceptor. It was first introduced in Angular 4.3 and it’s pretty useful.
Interceptors provide a method to intercept an outgoing request or incoming response. They are very similar to the middleware, with a framework like Express, except for the frontend. Interceptors can be very useful for features like caching and logging.
HttpInterceptor provides a way to intercept HTTP requests and responses to transform or handle them before passing them along.
Creating an interceptor is pretty simple. You have to create a class that is @injectable
and implements the HttpInterceptor
:
@Injectable()
export class ApiInterceptor implements HttpInterceptor {
constructor() {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({
//add original request and/or set/add new headers
});
});
return next.handle(authReq);
}
}
This takes req
and next
as a parameter and returns HttpEvent
as an observable.
After that you need to provide it in the AppModule
like this:
@NgModule({
bootstrap: [AppComponent],
imports: [...],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,
multi: true
}
]
})
export class AppModule {}
This is the basic implementation of HttpInterceptor
in a pretty simple way.
HttpInterceptors can perform a variety of tasks, from authentication to logging, setting or adding headers, HTTP request and response, notifications, error handling, and showing a loader on every HTTP request. Without interceptors, developers would have to add these tasks manually for every HTTP client call.
We can easily add authorization headers so we don’t have to add them separately in every request:
const addHeaders= req.clone({
headers: req.headers
.set('Authorization', `Bearer $token`)
.set('Content-Type', 'application/json')
});
return next.handle(addHeaders);
As you can see, it’s simple to manipulate headers in the interceptor.
We can add a loader in the interceptor for every request. For this, we add a loader service which has show and hide loader methods. We can then show the loader for every request and hide it when the request is finalised:
const loaderService = this.injector.get(LoaderService);
loaderService.show();
return next.handle(req).pipe(
delay(5000),
finalize(() => loaderService.hide())
);
This is a better solution — we don’t have to call the loader service with every single API request.
We can show a notification every time the response status is 200 or 201, which indicates success.
Here’s an example of object creation:
return next.handle(req).pipe(
tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse && event.status === 201) {
this.toastr.success("Object created.");
}
})
);
This will show the success toaster every time the response status is 201. This is pretty useful as you won’t have to explicitly add toaster in every API request.
There are multiple cases which can cause an error in interceptor. We can use the retry operator inside the pipe function (which combines multiple functions) for the http request.
We use Retry
from RxJS to resubscribe to the observable and in return we get a successful result:
return next.handle(req).pipe(
retry(2),
catchError((error: HttpErrorResponse) => {
if (error.status !== 401) {
// 401 handled in auth.interceptor
this.toastr.error(error.message);
}
return throwError(error);
})
);
We can check the status of error and on that basis we can decide what to do with it.
In the above example we’re using the retry
operator before checking the status of the exception. If the status isn’t 401 then we’re throwing a toaster error. So that’s how we will be handling error in interceptor.
With HttpInterceptors
, we can do other things too like profiling, URL manipulation and more.
That’s pretty much it.
Thank you for reading. I hope it helps!
#angular