Ionic 5 Make HTTP Calls and Handle Responses Easily Using an Interceptor

Applications with dynamic data call from REST API to get updated, these calls are mainly of POST or GET type. In Ionic 5 using Angular 9, we use HttpClientModule to make these POST or GET requests.

After Angular 4.3 the concept of Interceptors was introduced, using which we can override the HTTP calls to handle their requests and responses.

We can use Interceptors for many purposes like Handling Error responses, Internet connectivity intimations, show progress loaders, Add/ change header types of HTTP calls, secure server connectivity using token-based authentications, and many more.

In this post, we will learn How to make HTTP calls and handle their responses and request at one place using an Angular Interceptor.

We will create a new Ionic App using Angular latest stable version 7 using the latest Ionic CLI

Before we start make sure you have latest Ionic CLI installed

$ npm install -g @ionic/cli

 

Create new Ionic App

Run following NPM command in CMD to create a new Ionic application with a blank template.

$ ionic start ionic-interceptor-app blank --type=angular
$ cd ionic-interceptor-app

Add HttpClientModule in App’s Module

To use HTTP services, we need to import HttpClientModule, then add-in imports array in the app.module.ts file as shown below.

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Create an HTTP Interceptor

Now we will create an HTTP Interceptor to globally intercept and modify calls. Create a new file httpConfig.interceptor.ts and replace the below code in it.

Interceptors implements HttpInterceptor service to override the intercept method taking two parameters HttpRequest and HttpHandler.

//httpConfig.interceptor.ts
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpResponse,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { Injectable } from '@angular/core';

@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
  
  constructor() { }


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

    const token = "my-token-string-from-server";

    //Authentication by setting header with token value
    if (token) {
      request = request.clone({
        setHeaders: {
          'Authorization': token
        }
      });
    }

    if (!request.headers.has('Content-Type')) {
      request = request.clone({
        setHeaders: {
          '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;
      }),
      catchError((error: HttpErrorResponse) => {
        console.error(error);
        return throwError(error);
      }));
  }


}

As we can’t directly change headers of the request so we make a clone of request.

To use this interceptor globally we need to import this in the app.module.ts file and also add in providers array as shown below.

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { 
  HTTP_INTERCEPTORS, 
  HttpClientModule 
} from '@angular/common/http';
import { HttpConfigInterceptor } from './httpConfig.interceptor';


@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpConfigInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

 

Show Ionic Loader Spinner on HTTP request

Using Interceptors we can easily show/ hide Ionic Spinner loader at one place. We will import LoadingController and use it show/hide in the interceptor callbacks. You can check more details on loaders here

//httpConfig.interceptor.ts
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpResponse,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { LoadingController } from '@ionic/angular';

@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
  loaderToShow: any;
  constructor(
    public loadingController: LoadingController
    ) { }


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

    const token = "my-token-string-from-server";

    //Authentication by setting header with token value
    if (token) {
      request = request.clone({
        setHeaders: {
          'Authorization': token
        }
      });
    }

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

    request = request.clone({
      headers: request.headers.set('Accept', 'application/json')
    });
    this.showLoader();
    return next.handle(request).pipe(
      map((event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          console.log('event--->>>', event);
        }
        this.hideLoader();
        return event;
      }),
      catchError((error: HttpErrorResponse) => {
        console.error(error);
        this.hideLoader();
        return throwError(error);
      }));
  }

  showLoader() {
    this.loaderToShow = this.loadingController.create({
      message: 'Processing Server Request'
    }).then((res) => {
      res.present();

      res.onDidDismiss().then((dis) => {
        console.log('Loading dismissed!');
      });
    });
    this.hideLoader();
  }

  hideLoader() {
      this.loadingController.dismiss();
  }


}

Create a service HttpService

After adding the Interceptor, we will create a new service that will method to make an HTTP call to test our Interceptor response handling.

Create new service HttpService and replace the below code:

// http.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  apiUrl = 'https://localhost:8080/api/getdetails';

  constructor(private http: HttpClient) { }

  getDetails(): Observable<any> {
    return this.http.get(this.apiUrl)
      .pipe(
        tap(_ => this.log('response received')),
        catchError(this.handleError('getDetails', []))
      );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

  /** Log a HeroService message with the MessageService */
  private log(message: string) {
    console.log(message);
  }
}

 

Finally, in Home component replace following class and template code:

home.page.html

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic HTTP
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <ion-button (click)="employeeDetails()">
    Click me
  </ion-button>

</ion-content>

 

home.page.ts

// home.page.ts
import { Component } from '@angular/core';
import { HttpService } from '../http.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(private httpService:HttpService) { }

  employeeDetails(){
    this.httpService.getDetails().subscribe(books => {
      console.log(books);
    });
  }
}

 

So now we have an Interceptor in Ionic 4 application which is helping in modifying get requests by changing headers and also setting token in the Authentication key. We are also handling errors in Interceptors which can be logged for debugging purposes.

Subscribe
Notify of
guest
7 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Owen Lenegan

Hey bro, thanks so much for this, really neat way to implement API calls which require awkward Header info and some Heroku CORS injection in the testing phase. I’m a complete noob so if I can follow this and get it to work then you are clearly doing it right. Cheers buddy, appreciate your time and code *salute*

SereneScream

Appreciate the rundown on how to set this up. I’m running into an issue with an API I’m accessing though. With some requests it’s expecting a Cookie header. Testing in Postman this Cookie header is added in Postman’s temporary headers automatically. I’ve tried to add it manually mimicking how the the Authorization token is added is added in this example bit that just results in an error in the console “Refused to add unsafe header Cookie”. Any suggestions would be appreciated.

Crist

Sorry but something is missing in this tutorial, some part is not visible.
Please have a look,
thanks.

massimo

No work for me :
overlay does not exist

Anon

Can you post the http.service.ts code?