Angular: Handle HTTP Errors using Interceptors

By Maria Zayed

October 21st, 2021

Image

HTTP errors are common in Angular while making an HTTP request. It’s a good practice to handle these errors without hampering the user’s experience. In this article, we’ll discuss three ways of dealing with such cases.

First things first, let’s understand the interceptor

What is the Interceptor?

Interceptors are a unique type of Angular service that we can implement. Interceptors allow us to intercept incoming or outgoing HTTP requests using the HttpClient. By intercepting the HTTP request, we can modify or change the value of the request.

What is the Error Interceptor?

An error interceptor is a special type of interceptor which is used for handling errors that arise while making an HTTP request. The error may come in either the client-side (browser) or it may be an error from the server-side when the request fails due to any reason.

Let’s see the error interceptor in practice.

Project Setup.

First of all, let’s create a new project using the *ng new *command (I named the project http-interceptor).

After creating the project successfully, let create the needed component & services. Run the following commands:

  1. ng g c components/demo-component (to create a new component named demo)

  2. *ng g s services/api *(to create an api service)

  3. *ng g interceptor interceptors/error-catching *(to create an error-catching interceptor)

After running the above commands you should get a project structure like this.

Clean the *app.component.hrml *and add the * *tag.

In the *demo-component.component.html *add this piece of code.

<button (click)="getData()">Get Data</button>

<div **ngIf*="data">{{data}}</div>

Simple enough right! Nothing fancy happening here. Before starting with the fun part, let’s create a function to send an HTTP request.

// demo-component.component.ts

import {***Component***, OnInit} from '@angular/core';
import {ApiService} from "../../services/api.service";

@Component({
    selector: 'app-demo-component',
    templateUrl: './demo-component.component.html',
    styleUrls: ['./demo-component.component.scss']
})
export class DemoComponentComponent implements OnInit {

    data = ""

    constructor(private apiService: ApiService) {
    }

    ngOnInit(): void {
    }

    getData() {
        this.apiService.getData().subscribe(res => {
            this.data = ***JSON***.stringify(res)
        })
    }
}

// api.service.ts

import {***Injectable***} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";

@Injectable({
    providedIn: 'root'
})
export class ApiService {
    url = 'https://jsonplaceholder.typicode.com/todos/10';


    constructor(private http: HttpClient) {
    }

    getData(): Observable<any> {
        return this.http.get(this.url);
    }
}

At this point, it’s a typical HTTP request. NOW, let’s start with the fun part.

Use HTTP Interceptor

Now, let’s dive deep into the interesting part. First, let’s take a look at the *error-catching.interceptor.ts *file,

import {***Injectable***} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';

@Injectable()
export class ErrorCatchingInterceptor implements HttpInterceptor {

    constructor() {
    }

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        return next.handle(request);
    }
}

Notice that it implements the *HttpInterceptor *interface, which forces us to implement the intercept function which identifies and handles a given HTTP request.

What about the intercept function parameters?

  • *req *— The outgoing request object to handle.

  • *next *— The next interceptor in the chain, or the backend if no interceptors remain in the chain.

  • Returns: an observable of the event stream.

Now let’s add our touch.

Take a look at the next code.

import {***Injectable***} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError} from "rxjs/operators";

@Injectable()
export class ErrorCatchingInterceptor implements HttpInterceptor {

    constructor() {
    }

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        return next.handle(request)
            .pipe(
                catchError((error: HttpErrorResponse) => {
                    let errorMsg = '';
                    if (error.error instanceof ***ErrorEvent***) {
                        ***console***.log('This is client side error');
                        errorMsg = `Error: ${error.error.message}`;
                    } else {
                        ***console***.log('This is server side error');
                        errorMsg = `Error Code: ${error.status},  Message: ${error.message}`;
                    }
                    ***console***.log(errorMsg);
                    return throwError(errorMsg);
                })
            )
    }
}

What is happening here? I’ll explain.

Inside the *pipe() *operator of rxjs we are catching any error and checking for the source of it (whether it’s from the client-side or from the server-side). Then we are returning the error through the throwError() operator of rxjs.

Now, let’s use the interceptor file in our project. We will add it to the *providers *array in the app.module.ts.

@NgModule({
    ...
    providers: [
        {
            provide: ***HTTP_INTERCEPTORS***,
            useClass: ErrorCatchingInterceptor,
            multi: true
        }
    ],
    ...
})
export class AppModule {
}

Let’s make sure that everything is working as expected.

What is the expected behavior? The expected behavior is every request/response should pass through the interceptor.

For the sake of the demo, we will modify the *error-catching.interceptor.ts *file, and add two console.log().

import {***Injectable***} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, map} from "rxjs/operators";

@Injectable()
export class ErrorCatchingInterceptor implements HttpInterceptor {

    constructor() {
    }

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        ***console***.log("Passed through the interceptor in request");

        return next.handle(request)
            .pipe(
                map(res => {
                    ***console***.log("Passed through the interceptor in response");
                    return res
                }),
                catchError((error: HttpErrorResponse) => {
                    let errorMsg = '';
                    if (error.error instanceof ***ErrorEvent***) {
                        ***console***.log('This is client side error');
                        errorMsg = `Error: ${error.error.message}`;
                    } else {
                        ***console***.log('This is server side error');
                        errorMsg = `Error Code: ${error.status},  Message: ${error.message}`;
                    }
                    ***console***.log(errorMsg);
                    return throwError(errorMsg);
                })
            )
    }
}

You should see the following output.

Now, let’s break the URL to make the request fail.

url = 'https://jsonplaceholder.typicode.com/tod';

Now, after running the code with a broken URL you should see the following output.



Continue Learning