Introduction To Angular HTTP Interceptors & How To Use Them?

This blog gives a clear insight into the Angular HTTP Interceptors and the ways of using them. It starts with introducing HTTP Interceptors and some common use cases of them. It discusses creating basic HTTP interceptors and ways to create them on Angular. In the latter stage, it discusses the ways to use them to manage HTTP requests.

Introduction To Angular HTTP Interceptors & How To Use Them?

Angular is used to build JavaScript-heavy single-page based web applications. Single page apps or SPA’s load the entire content of a site within a single page. This single page is usually an index.html file. Today, we will talk about Angular HTTP Interceptors in detail.

It allows us to deliver rich, dynamic, and fast-loading content that mimics that of desktop applications. Angular expects the browser to build the page. The server sends the browser all the data to build the page which includes the HTML, CSS, and JavaScripts files.

Angular allows us to write much more succinct code than using plain JavaScript. We can add If conditions, loops, and local variables directly within our templates.

Single Page Applications load the full site content within a single page using an index.html file such that once the page is loaded, we can update a section within that page by clicking on them. Due to the AngularJS framework, we need fewer lines of code.

Overview of Angular HTTP Interceptor?

HttpInterceptor came along with Angular 4.3. It gives us a way to intercept HTTP requests and responses to transform or handle them before passing them along.

As per Angular Docs, “Although Angular HTTP Interceptors are capable of mutating requests and responses, the HttpRequest and HttpResponse instance properties are read-only, rendering them largely immutable.”

This may be due to the fact that we should try to retry a request, in case it is unsuccessful at first.

Angular Docs goes on to say “Angular applies interceptors in the order that you provide them. If you provide interceptors A, then B, then C, requests will flow in A->B->C and responses will flow out C->B->A.

You cannot change the order or remove Angular Interceptors later. If you need to enable and disable an interceptor dynamically, you’ll have to build that capability into the interceptor itself. “

You must keep this in mind when using multiple Angular HTTP Interceptors.

Common Use Cases of Interceptors

Interceptors are methods to do some work for every HTTP request. Here are a few examples of common use cases for interceptors:

  • Add a token or some custom HTTP header for all outgoing HTTP requests
  • Catch HTTP responses to do custom formatting (i.e. convert CSV to JSON) before transferring the data to your service/component
  • Log all HTTP activity in the console

Creating A Basic HTTP Interceptor

Angularjs-cta-first

Creating A Service

To start things off, you must create a service that implements HttpInterceptor.

import { HttpInterceptor} from '@angular/common/http';
import { Injectable } from '@angular/core';@Injectable()
export class TokenInterceptorService implements HttpInterceptor {   intercept(req: HttpRequest<any>, next: HttpHandler):   Observable<HttpEvent<any>> {
       // All HTTP requests are going to go through this method
   }
}

To call that interceptor, it has to be added to the list of all HTTP_INTERCEPTORS:

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

Since there could be a varied number of interceptors, we give our interceptor service with “multi:true”

Intercepting a Request

Once we have set-up our interceptor, we need to implement the intercept method to handle a token. An HTTP header is added, If our LoginService has a auth token:

export class TokenInterceptorService implements HttpInterceptor {
      // We inject a LoginService
   constructor(private loginService: LoginService) {}   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {      console.log('INTERCEPTOR');
      // We retrieve the token, if any
      const token = this.loginService.getAuthToken();
      let newHeaders = req.headers;
      if (token) {
         // If we have a token, we append it to our new headers
         newHeaders = newHeaders.append('authtoken', token);
      }
      // Finally we have to clone our request with our new headers
      // This is required because HttpRequests are immutable
      const authReq = req.clone({headers: newHeaders});      // Then we return an Observable that will run the request
      // or pass it to the next interceptor if any
      return next.handle(authReq);
   }
}

Thus, every time you make an HTTP request, the code will automatically add that header.

Intercepting a Response

response

This is done through the same intercept method in our Angular HTTP Interceptors. The difference is that the work we want to do with the response has to be registered as a side-effect on the Observable we return from the intercept method.

In simple terms, this means that when we work with an HTTP response, we have to use RxJs operators and the Pipe Method:

return next.handle(authReq).pipe(
// Do custom work here
);

Further to this, we have shown how this can be done by replacing the HTTP response with some fake data for testing purposes only:

return next.handle(authReq).pipe(
// We use the map operator to change the data from the response
map(resp => {
// Several HTTP events go through that Observable
// so we make sure that this is an HTTP response
if (resp instanceof HttpResponse) {
// Just like for request, we create a clone of the response
// and make changes to it, then return that clone
return resp.clone({ body: [{title: 'Replaced data in interceptor'}] });
}
})
);

Ways to Use Interceptors in Angular

Authentication

password security authentication

Having a well-established authentication system in place is essential for Angular Interceptors. It can be used for:

  • Adding bearer tokens
  • Adding refresh tokens
  • Redirecting to the login page

When sending the bearer token, some filtering is required. In case we don’t have a token, then it’s possible that we’re logging in, and do not have a token.

Caching

Caching

It i important to note that there are some interceptors that can handle requests by themselves. They do not need forwarding to next.handle() and we can use them for caching requests.

We do this by using the URL to key in our cache. We can then return an observable if we find a response in the map, by-passing the “next” handler.

You can also improve your performance by doing so, as you don’t have to access the backend when you already have the response cached for Interceptor Angular.

import { Injectable } from '@angular/core';
 	import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse } from '@angular/common/http';
 	import { Observable, of } from 'rxjs';
 	import { tap, shareReplay } from 'rxjs/operators';
 	
 	@Injectable()
 	export class CacheInterceptor implements HttpInterceptor {
 	  private cache = new Map<string, any>();
 	

      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 	    if (request.method !== 'GET') {
 	      return next.handle(request);
 	    }
 	
        const cachedResponse = this.cache.get(request.url);
 	    if (cachedResponse) {
 	      return of(cachedResponse);
 	    }
 	
        return next.handle(request).pipe(
 	      tap(event => {
 	        if (event instanceof HttpResponse) {
 	          this.cache.set(request.url, event);
 	        }
 	      })
 	    );
 	  }
 	}

If you run the request, then click clear response, and then run again, you will use the cache.

Fake Backend

In case you don’t have a backend ready, a fake one can be utilised for development. All you have to do is mock the Response depending on the request, and then return an Observable to HttpResponse.

const body = {
  firstName: "Mock",
  lastName: "Faker"
};return of(new HttpResponse(
  { status: 200, body: body }
));

Profiling

Angular HTTP Interceptors can log an entire HTTP operation due to the fact that they can process the request and respond together.

We can then get the timing of the request and the response and log the outcome with the time taken.

const started = Date.now();
 	let ok: string;
 	
    return next.handle(req).pipe(
 	  tap(
 	    (event: HttpEvent<any>) => ok = event instanceof HttpResponse ? 'succeeded' : '',
 	    (error: HttpErrorResponse) => ok = "failed"
 	  ),
 	  // Log when response observable either completes or errors
 	  finalize(() => {
 	    const elapsed = Date.now() - started;
 	    const msg = `${req.method} "${req.urlWithParams}" ${ok} in ${elapsed} ms.`;
 	    console.log(msg);
 	  })
 	);

Errors

Errors

The use of errors can be implemented in two instances in the Angular HTTP Interceptors. For instance, network interferences are many in mobile situations, and attempting again may finally be victorious. This is important for Interceptor Angular.

Interesting points here are how frequently to retry before surrendering. Also, would it be advisable for us to hold up before retrying or do it right away?

One way is to retry the HTTP call. To get this done, we can use the retry operator from RxJS which can help us re-subscribe to the observable. This will reissue the request.

Otherwise, you can use the status of the exception. What you do, will depend on this.

return next.handle(req).pipe(
 	  retry(2),
 	  catchError((error: HttpErrorResponse) => {
 	    if (error.status !== 401) {
 	      // 401 handled in auth.interceptor
 	      this.toastr.error(error.message);      
 	    }
 	    return throwError(error);
 	  })
 	);

Before checking the error status in the example below, we retry twice. In the case of no 401 error, we show the error as a popup. All the other errors are then re-thrown for handling.

Notifications

Notifications

Here, we have a wide range of situations where we could show messages. In the example below, “OBJECT CREATED” is shown each time we get a 201 status over from the server.

return next.handle(req).pipe(
 	  tap((event: HttpEvent<any>) => {
 	    if (event instanceof HttpResponse && event.status === 201) {
 	      this.toastr.success("Object created.");
 	    }
 	  })
 	);

To check the type of object, we can show “TYPE CREATED”. We can also show more precise messages, by wrapping our data in an object together with a message.

Headers

Here are a few things that can be accomplished by manipulating headers:

  • Authentication/authorization
  • Caching behavior; for example, If-Modified-Since
  • XSRF protection

Here is how you can add a header to the request in the interceptor:

const modified = req.clone({
  setHeaders: { "X-Man": "Wolverine" }
});return next.handle(modified);

Angular uses interceptors for insurance against Cross-Site Request Forgery (XSRF). It does this by perusing the XSRF-TOKEN from a cookie and setting it as the X-XSRF-TOKEN HTTP header.

Since the cookie can be read-only by the code that runs on your domain, the backend can be certain that the HTTP demand originated from your customer application and not an assailant.

Converting

Angularjs-cta-second
At the point when the API restores an arrangement we don’t concur with, we can utilize an interceptor to design it the manner in which we like it.

This could be changing over from XML to JSON or like in this model property names from PascalCase to camelCase. We can also utilize an interceptor to rename all the property names to camelCase.

Check if there is an npm bundle that can do the truly difficult work for you. In the example below, we are utilizing mapKeys and camelCase from lodash.

return next.handle(req).pipe(
 	  map((event: HttpEvent<any>) => {
 	    if (event instanceof HttpResponse) {
 	      let camelCaseObject = mapKeys(event.body, (v, k) => camelCase(k));
 	      const modEvent = event.clone({ body: camelCaseObject });
 	      
 	      return modEvent;
 	    }
 	  })
 	);

Loader

You could set up a loader centrally in an interceptor so that it shows up whenever there are active requests. You can use a “loader service” for this, and make sure that it has a show and hide function.

const loaderService = this.injector.get(LoaderService);
 	loaderService.show();
 	 	return next.handle(req).pipe(
 	  delay(5000),
 	  finalize(() => loaderService.hide())
 	);

We should consider that there could be numerous HTTP calls intercepted. This could be worked through, by having a counter for requests (+1) and responses (- 1).

URL

Below, let’s see how easy or difficult it is to manipulate the URL. Let us take for instance if you want to change HTTP to HTTPS. You can use cloning and replacing, and send the cloned request to the next handler.

// clone request and replace 'http://' with 'https://' at the same time
 	const httpsReq = req.clone({
 	  url: req.url.replace("http://", "https://")
 	});
 	return next.handle(httpsReq);

Using Angular Interceptors to Manage HTTP Requests

The Angular Interceptor was introduced in version 4.3. It is used to handle HTTP responses and requests.

Prerequisites

Prerequisites

Before you begin, however, make sure you have Node.JS installed locally. Check  this link to see how you can do that. You must also be well-versed with Angular CLI to create Angular apps.

Creating the Angular App

You must run the command given below, to create a new Angular app named “Angular-Interceptor” with the CLI.

npx @angular/[email protected] new Angular-Interceptor

Soon, you will see some prompts. Below, is what was selected for this particular instance.

Output

Would you like to add Angular routing? Yes

Which stylesheet format would you like to use? SCSS   [ http://sass-lang.com   ]

Now, you must go to your new project directory and serve the project:

  • cd Angular-Interceptor
  • npx ng serve –open

To view the app, view http://localhost:4200  in your browser. This is now, your basic Angular app.

Styling the Angular App

Once you’ve done the basics, styling will help you make the user experience better.

From the get-go, you will require an Angular Material component. You can find instructions for the same here. Next, you must run the command given below:

npm install –save @angular/[email protected] @angular/[email protected] @angular/[email protected]

The next step will be to add animations. To do this, you must open the src/app/app.module.ts file in your code editor and add the code given below to import BrowserAnimationsModule:

src/app/app.module.ts

...

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({

  ...

  imports: [

    ...

    BrowserAnimationsModule

  ],

  ...

})

export class AppModule { }

Once this is done, 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 { }

To make the UI even better, you can add a theme to it. Add indigo-pink.css to your styles.scss file:

src/styles.scss

@import "[email protected]/material/prebuilt-themes/indigo-pink.css";

Creating an Angular Interceptor

Creating an Angular Interceptor

Now that the app is stylized, you must create the Angular HTTP Interceptors. To do this, begin with making a new folder for the interceptor, under the app folder.

You can then make a new file – httpconfig.interceptor.ts under the Angular Interceptors folder. Then you must import the dependence given below into this 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';

Now, you must create a class and implement the interface. For instance,

@Injectable()

export class HttpConfigInterceptor implements HttpInterceptor {

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

      // ...

    }

}

Next, set-up a token, Content-Type, Accept type for an API request. You will find an example below:

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

Here is how you can handle the API response:

map((event: HttpEvent<any>) => {

    if (event instanceof HttpResponse) {

        console.log('event--->>>', event);

    }

    return event;

}),

The next move would be to 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

  ],

  ...

})

Once done, add HttpConfigInterceptor to providers.

Creating a Service for Error Handling Presently, you will make the errorDialogService to deal with errors and show the error message for the user.

Make another error dialog under the application folder. Here, you can make your errorDialogService files.

To deal with the error response, make another errordialog.service.ts record and include the code beneath:

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, we need to display the dialog for the users. To this end, create errordialog.component.ts

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

}

The next step is to 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>

Another visit to httpconfig.interceptor.ts is required to handle the error response. You can begin by importing errordialog.service:

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

Add a constructor for errorDialogService:

constructor(public errorDialogService: ErrorDialogService) { }

Use this code handle the error response with 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);

})

Now you can 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],

})

Creating a Service File for HTTP Requests

For this, you will need two things – a login API and a Customer Detail API.
Angularjs-cta-third
You must first create a new “services” folder under the “src” folder. Then, under the “services” folder, make a new login.service.ts file. Add the functions below for the 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');

    } 

}

Read also: Exploring The 40 Angularjs Dashboard Templates For Front-End Development

Invoking HTTP Client Service

To invoke the HTTP client service in the app component, you must add the two “LoginService” functions to it.  Call the 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);

    });

  }

}

Remember to include the element for the user to click on in app.component.html:

src/app/app.component.html

...

<h2 (click)="getCustomerDetails()">Get customer details</h2>

Now you can add LoginService to providers in your AppModule:

src/app/app.module.ts

...

import { LoginService } from './services/login.service';

...

@NgModule({

  ...

  providers: [

    ...

    LoginService

  ]

  ...

})

Conclusion

As you may have concluded, Angular Interceptors were a good addition to Angular 4.3 and had varied creative uses. We hope you can use this knowledge as a base to unleash more of your creativity with Angular!

We hope you had a great time reading this article and it proves to be of great value for any AngularJS Development Company in the long run. Thank You.!

Frequently Asked Questions

It is for providing an interceptor the part where there is a statement: Note the multi: true option.
It ensures that one request per token is processed regularly.
The HttpClient is used for performing HTTP requests. It’s an easy alternative to traditional HTTP.
Chintan Gor
Chintan Gor
Enthusiastic for web app development, Chintan Gor has zeal in experimenting with his knowledge of Node.js in various aspects of development. He keeps on updating his technical know-how thus pinning his name among the topmost CTO's in India. His contribution is penned down by him through various blogs on Node.js. He is associated with eSparkBiz - a Node.js Development Company from the past 7-8 years where one can get premium services.
Blog CTA
  • This field is for validation purposes and should be left unchanged.

Related Post

Step-by-Step Guide To Extending A Controller In Angularjs

Step-by-Step Guide To Extending A Controller In Angularjs

AngularJS is a structural JavaScript MVC framework. If you are looking to ...

Diving Into Angular JS Performance Tuning Tips Of All Time

Diving Into Angular JS Performance Tuning Tips Of All Time

Web designing has been going through several changes ever since its incept...

Surveying The Best IDE for AngularJS Development In Detail

Surveying The Best IDE for AngularJS Development In Detail

Angular JS is an open-source JavaScript front-end framework. Developers ma...

Your information is safe with us. eSparkBiz guarantees 100% data security. We don’t use emails for spamming.

  • By clicking Inquire Now, you accept eSparkBiz's Terms of Use and Privacy Policy and agree to your personal data collection and use.
  • This field is for validation purposes and should be left unchanged.