Use cases
A common use case for catching errors from an API in Angular is centralized HTTP error handling. The interceptor can capture all errors returned by HTTP requests to the API and take actions based on the error type or server response
How it works ?
The operation of an interceptor is very simple. Here is a diagram that should help you understand :
The interceptor will simply intercept requests passing between the client and the server. We will therefore be able to define or not actions when intercepting requests.
How to do ?
1 - Create your interceptor
To create your Interceptor you can use Angular CLI :
$ ng generate interceptor http-error
Once your file is created, it should look more or less like this :
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(request);
}
}
2 - Intercept HTTP request errors
Currently your interceptor detects all passing and outgoing HTTP requests. But we want to detect errors returned by the API
To do this, we will have to use an Rxjs operator: catchError()
. This operator will catch errors within an Observable and return them.
Let's start by importing catchError()
:
import { catchError } from 'rxjs/operators';
Now that it's imported, let's use it in our intercept function:
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(() => error);
})
);
}
💡
pipe()
allows you to open an operation pipeline, and to use RxJS operators.
💡throwError()
create an Error Observable.
3 - React to errors
Now that we catch our errors returned by our API, we can react to them. The most common case is to display a toast of the error in question:
In my case I use angular2-notifications
, but the operation will remain generally the same with other packages.
So, I import it:
import { NotificationsService } from 'angular2-notifications';
I inject it:
constructor(public notifications: NotificationsService) {}
And now I display my error toast:
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
this.notifications.error(error.error.message);
return throwError(() => error);
})
);
}
⚠️ My API returns an Object with the message attribute which contains the error in question. You will need to adapt this part based on feedback from your API.
4 - Declare the interceptor
Now that the interceptor is complete, it must be declared. To do this, you will need to go to the module where you wish to use it and import your interceptor and HTTP_INTERCEPTORS
and declare it:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpErrorInterceptor } from '@core/interceptors/http-error.interceptor';
@NgModule({
...
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true
}
]
...
})
💡
multi: true
adds the interceptor to the interceptor array, rather than replacing them.
How to bypass the interceptor?
If for a particular reason you do not want the interceptor to intercept certain requests, you can bypass it using the httpBackend
module.
💡 The interceptors are located between the HttpClient and HttpBackend interface. So the trick is we need to inject HttpBackend directly into our service and create a new HttpClient using HttpBackend
To begin, you must import the HttpBackend
module into your service in question:
import { HttpBackend } from '@angular/common/http';
Then, inject it, and declare it:
constructor(
private handler: HttpBackend,
) {
this.httpClient = new HttpClient(handler);
}
Now, when using this.httpClient
the requests will not go through the interceptor.
Exemple :
constructor(
private handler: HttpBackend,
) {
this.httpClient = new HttpClient(handler);
}
getUsers(): Observable<User[]> {
return this.httpClient.get<User[]>('/api/users');
}
If getUsers()
returns an error, it will not be caught by the interceptor.
Top comments (0)