Tutorial

How To Use Angular Interceptors to Manage HTTP Requests and Error Handling

Updated on May 12, 2020
Default avatar

By vignesh

How To Use Angular Interceptors to Manage HTTP Requests and Error Handling

This tutorial is out of date and no longer maintained.

Introduction

Angular Interceptors may be familiar to AngularJS developers, but Interceptors weren’t supported in early Angular versions. The Angular Interceptor was introduced in version 4.3 and is used to handle HTTP responses and requests. In this post, you will learn how to use the Angular 6 and 7 Interceptor to handle the HTTP request, response, and error handling.

Warning: Several of the packages in this tutorial now contain dependencies with known vulnerabilities. In a production setting you would resolve these issues by upgrading these packages, finding alternatives, or creating forked versions with patched fixes. However, within the limited context of a tutorial, it provides educational value as-is.

Prerequisites

To complete this tutorial, you will need:

At the time of writing, this tutorial used Node v8.12.0 and npm v6.4.1, but the tutorial has been verified with Node v14.2.0 and npm v6.14.4.

Note: This tutorial was written to connect to an sample API. However, the steps for creating and serving this backend is outside of the scope of this tutorial and left as an exercise to the reader.

Step 1 — Creating the Angular App

First, create a new Angular app named Angular-Interceptor with the CLI by running the following command in your terminal window:

  1. npx @angular/cli@7.0.6 new Angular-Interceptor

You will be prompted to make a few selections for the project. For this tutorial, the following choices were selected:

Output
Would you like to add Angular routing? Yes Which stylesheet format would you like to use? SCSS [ http://sass-lang.com ]

Next, navigate to your new project directory and serve the project:

  1. cd Angular-Interceptor
  2. npx ng serve --open

Then, view http://localhost:4200 in your browser to see the app. You’ve now configured a basic Angular app.

Step 2 — Styling the Angular App

Now that you’ve set up the basic project, let’s do a few things to enhance the user experience.

First you will add an Angular Material component for a better UI experience. You can find instructions on setting this up on the Angular Material Getting Started page

In your terminal window, run the following command to add @angular/material, @angular/cdk, and @angular/animations:

  1. npm install --save @angular/material@7.0.4 @angular/cdk@7.0.4 @angular/animations@7.0.4

Next you will configure animations. Open the src/app/app.module.ts file in your code editor and add the following code to import BrowserAnimationsModule:

src/app/app.module.ts
...
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  ...
  imports: [
    ...
    BrowserAnimationsModule
  ],
  ...
})
export class AppModule { }

Now you’re ready to import the component modules. Add the following code to import the MatDialogModule in your src/app/app.module.ts file:

src/app/app.module.ts
...
import { MatDialogModule } from '@angular/material';

@NgModule({
  ...
  imports: [
    ...
    MatDialogModule
  ],
  ...
})
export class AppModule { }

Let’s also add a theme to enhance the UI. Add indigo-pink.css to your styles.scss file:

src/styles.scss
@import "~@angular/material/prebuilt-themes/indigo-pink.css";

In this step you stylized your Angular app. In the next step, you will create an Interceptor.

Step 3 — Creating an Angular Interceptor

First, create a new interceptor folder under the app folder. Then create a new httpconfig.interceptor.ts file under the interceptor folder.

Import the following dependencies into your httpconfig.interceptor.ts file:

src/app/intreceptor/httpconfig.interceptor.ts
import { Injectable } from '@angular/core';
import {
    HttpInterceptor,
    HttpRequest,
    HttpResponse,
    HttpHandler,
    HttpEvent,
    HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

Note: In Angular 6 and 7, map was changed from rxjs/add/operator/map to rxjs/operators. You need to be careful while importing. These are the main changes in Angular 6 and 7.

Create a class HttpConfigInterceptor and implement the interface HttpInterceptor. This is an example:

@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      // ...
    }
}

You will set token, Content-Type, Accept type for an API request. Here is an example:

const token: string = localStorage.getItem('token');

request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });

request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });

request = request.clone({ headers: request.headers.set('Accept', 'application/json') });

You will need to handle the API response. This is an example:

            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                }
                return event;
            }),

Here’s the full code snippet:

src/app/interceptor/httpconfig.interceptor.ts
...
@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const token: string = localStorage.getItem('token');

        if (token) {
            request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
        }

        if (!request.headers.has('Content-Type')) {
            request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
        }

        request = request.clone({ headers: request.headers.set('Accept', 'application/json') });

        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                }
                return event;
            }));
    }
}

Next, import the httpconfig.interceptor.ts in your AppModule.

src/app/app.module.ts
...
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
...
import { HttpConfigInterceptor } from './interceptor/httpconfig.interceptor';

@NgModule({
  ...
  imports: [
    ...
    HttpClientModule
  ],
  ...
})

Then, add HttpConfigInterceptor to providers. To handle multiple interceptors, add multi: true.

src/app/app.module.ts
...
@NgModule({
  ...
  providers: [
    ...
    { provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true }
  ],
  ...
})
...

Step 4 — Creating a Service for Handling Errors

Now, you will create the errorDialogService to handle errors and display the error message for users.

Create a new error-dialog folder under the app folder. Under the error-dialog folder, create your errorDialogService files.

To handle the error response, create a new errordialog.service.ts file and add the code below:

src/error-dialog/errordialog.service.ts
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { ErrorDialogComponent } from './errordialog.component';

@Injectable()
export class ErrorDialogService {
    public isDialogOpen: Boolean = false;
    constructor(public dialog: MatDialog) { }
    openDialog(data): any {
        if (this.isDialogOpen) {
            return false;
        }
        this.isDialogOpen = true;
        const dialogRef = this.dialog.open(ErrorDialogComponent, {
            width: '300px',
            data: data
        });

        dialogRef.afterClosed().subscribe(result => {
            console.log('The dialog was closed');
            this.isDialogOpen = false;
            let animal;
            animal = result;
        });
    }
}

Next, create errordialog.component.ts to display the error dialog for the users:

src/error-dialog/errordialog.component.ts
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material';

@Component({
  selector: 'app-root',
  templateUrl: './errordialog.component.html'
})
export class ErrorDialogComponent {
  title = 'Angular-Interceptor';
  constructor(@Inject(MAT_DIALOG_DATA) public data: string) {}
}

Then, create the errordialog.component.html template:

src/error-dialog/errordialog.component.html
<div>
    <div>
        <p>
            Reason: {{data.reason}}
        </p>
        <p>
            Status: {{data.status}}
        </p>
    </div>
</div>

To handle the error response, you will need to revisit httpconfig.interceptor.ts.

Start with importing errordialog.service:

import { ErrorDialogService } from '../error-dialog/errordialog.service';

Add a constructor for errorDialogService:

constructor(public errorDialogService: ErrorDialogService) { }

The code below will handle the error response using catchError and throwError:

            catchError((error: HttpErrorResponse) => {
                let data = {};
                data = {
                    reason: error && error.error && error.error.reason ? error.error.reason : '',
                    status: error.status
                };
                this.errorDialogService.openDialog(data);
                return throwError(error);
            })

Here’s the full code snippet:

src/app/interceptor/httpconfig.interceptor.ts
...
import { ErrorDialogService } from '../error-dialog/errordialog.service';
...
@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
    constructor(public errorDialogService: ErrorDialogService) { }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        ...
        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                }
                return event;
            }),
            catchError((error: HttpErrorResponse) => {
                let data = {};
                data = {
                    reason: error && error.error && error.error.reason ? error.error.reason : '',
                    status: error.status
                };
                this.errorDialogService.openDialog(data);
                return throwError(error);
            }));
    }
}

Then, import errordialog.service and errordialog.component into the AppModule:

src/app/app.module.ts
...
import { ErrorDialogComponent } from './error-dialog/errordialog.component';
...
import { ErrorDialogService } from './error-dialog/errordialog.service';
...
@NgModule({
  ...
  declarations: [
    ...
    ErrorDialogComponent
  ],
  ...
  providers: [
    ...
    ErrorDialogService
  ],
  entryComponents: [ErrorDialogComponent],
})

Step 5 — Creating a Sample Service File for HTTP Requests

In this example you will need a sample service file for API calls:

  • Login API
  • Customer Detail API

Make a new services folder under the src folder. Create a new login.service.ts file under the services folder. And add the functions to call two APIs.

src/services/login.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class LoginService {

    constructor(private http: HttpClient) { }

    login(data) {
        data = { email: 'admin', password: 'admin' };
        return this.http.post('http://localhost:3070/api/login', data);
    }

    getCustomerDetails() {
        return this.http.get('http://localhost:3070/customers/details');
    }

}

Note: In this example, there is a separate backend which is running in localhost:3070.

Step 6 — Invoking the HTTP Client Service in the App Component

Add the two LoginService functions to app.component.ts. Call login API with onload and the customers/details with onclick.

src/app/app.component.ts
import { Component } from '@angular/core';
import { LoginService } from './services/login.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'Angular-Interceptor';
  constructor(public loginService: LoginService) {
    this.loginService.login({}).subscribe(data => {
      console.log(data);
    });
  }

  getCustomerDetails() {
    this.loginService.getCustomerDetails().subscribe((data) => {
      console.log('----->>>', data);
    });
  }
}

Also, add the element for the user to click on in app.component.html:

src/app/app.component.html
...
<h2 (click)="getCustomerDetails()">Get customer details</h2>

Then, add LoginService to providers in your AppModule:

src/app/app.module.ts
...
import { LoginService } from './services/login.service';
...
@NgModule({
  ...
  providers: [
    ...
    LoginService
  ]
  ...
})

Here is a screenshot of the error handler dialog:

Error Handling Dialog

Conclusion

In this tutorial, you learned about how to handle the HTTP request and response using Angular 6 & 7 interceptors, along with how to handle the error using Angular Material dialog.

You can find the sample code on GitHub. You can git clone, npm install, and npx ng server to run the sample code. Don’t forget to change the backend API URL in the login.service.ts file.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
vignesh

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel