Angular - Setup an Interceptor for HTTP request error.
Tags: angular, webdev, programming, javascript
Published: 2024-03-04 20:48:40


<h3><u>Use cases</u></h3>
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

<h3><u>How it works ?</u></h3>
The operation of an interceptor is very simple. Here is a diagram that should help you understand :

![interceptor image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gsmtcnx8kg78fn6u1cef.png)
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.

<h3><u>How to do ?</u></h3>
**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 :

```TypeScript
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()` :

```TypeScript
import { catchError } from 'rxjs/operators';
```
Now that it's imported, let's use it in our intercept function:

```TypeScript
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:

![error toast image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehgoen219y275ic7anf2.png)
In my case I use `angular2-notifications`, but the operation will remain generally the same with other packages.
So, I import it:

```TypeScript
import { NotificationsService } from 'angular2-notifications';
```
I inject it:

```TypeScript
constructor(public notifications: NotificationsService) {}
```
And now I display my error toast:

```TypeScript
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:

```TypeScript
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.

<h3><u>How to bypass the interceptor?</u></h3>
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:

```TypeScript
import { HttpBackend } from '@angular/common/http';
```
Then, inject it, and declare it:

```TypeScript
constructor(
    private handler: HttpBackend,
) {
    this.httpClient = new HttpClient(handler);
}
```
Now, when using `this.httpClient` the requests will not go through the interceptor.

Exemple : 

```TypeScript
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.