Angular - Setup an Interceptor for HTTP request error.

theobourdel

Théo

Posted on March 4, 2024

Angular - Setup an Interceptor for HTTP request error.

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 :

interceptor image
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


Enter fullscreen mode Exit fullscreen mode

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);
  }
}


Enter fullscreen mode Exit fullscreen mode

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';


Enter fullscreen mode Exit fullscreen mode

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);
      })
    );
  }


Enter fullscreen mode Exit fullscreen mode

💡 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:

error toast image
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';


Enter fullscreen mode Exit fullscreen mode

I inject it:



constructor(public notifications: NotificationsService) {}


Enter fullscreen mode Exit fullscreen mode

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);
      })
    );
  }


Enter fullscreen mode Exit fullscreen mode

⚠️ 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
      }
   ]
   ...
})


Enter fullscreen mode Exit fullscreen mode

💡 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';


Enter fullscreen mode Exit fullscreen mode

Then, inject it, and declare it:



constructor(
    private handler: HttpBackend,
) {
    this.httpClient = new HttpClient(handler);
}


Enter fullscreen mode Exit fullscreen mode

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');
}


Enter fullscreen mode Exit fullscreen mode

If getUsers() returns an error, it will not be caught by the interceptor.

💖 💪 🙅 🚩
theobourdel
Théo

Posted on March 4, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related