HTTP Interceptors in Angular

In Angular, HTTP interceptors are a powerful feature that allows you to intercept and modify HTTP requests and responses at a centralized location. They act as middleware, sitting between the application’s HTTP client (typically the built-in HttpClient module) and the server.

What is an HTTP Interceptor?

HTTP Interceptors are a middleware mechanism in Angular’s HttpClient module that intercepts HTTP requests and responses. They allow you to intercept outgoing HTTP requests or incoming HTTP responses and perform operations such as modifying request headers, handling errors, adding authentication tokens, caching responses, and more.

Features of HTTP Interceptor

  • Request Modification: Interceptors can modify outgoing HTTP requests before they are sent to the server. This includes adding headers, transforming request bodies, or even cancelling requests altogether.
  • Response Modification: Interceptors can also modify incoming HTTP responses before they reach the application code. This can involve transforming response data, handling errors, or adding custom logic based on the response.
  • Global Applicability: Interceptors are registered at the module level and are automatically applied to all HTTP requests and responses within the application, ensuring consistent behavior across different components and services.
  • Chaining: Multiple interceptors can be registered and chained together, allowing for modular and reusable code.
  • Dependency Injection: Interceptors can be injected with other services or dependencies, enabling more complex logic and integration with other parts of the application.

Uses of HTTP Interceptor

  • Authentication: Interceptors can be used to automatically attach authentication tokens or credentials to outgoing requests, ensuring secure communication with the server.
  • Error Handling: Interceptors can centralize error handling logic, catching and processing errors from HTTP responses, and providing custom error handling or logging mechanisms.
  • Caching: Interceptors can implement caching strategies by intercepting requests and responses, reducing redundant data fetching and improving application performance.
  • Logging: Interceptors can be used to log HTTP requests and responses for debugging or auditing purposes.
  • Content Transformation: Interceptors can transform request and response data, such as serializing or deserializing data formats (e.g., JSON, XML).
  • Cross-cutting Concerns: Interceptors can encapsulate cross-cutting concerns related to HTTP communication, such as retry logic, rate limiting, or performance monitoring.

Limitations of HTTP Interceptor

  • Complexity: While interceptors are powerful tools, using too many or implementing them incorrectly can make your code harder to maintain and understand.
  • Ordering: When you have multiple interceptors set up, the order in which they run can matter. If the order is wrong, you might end up with unexpected behaviors or conflicts.
  • Performance Overhead: Interceptors add some extra processing for each HTTP request and response. If not done efficiently, this extra work can slow down your application, especially if it has a lot of traffic.
  • Limited to HttpClient: Interceptors in Angular only work with HTTP requests made using the built-in HttpClient module. They won’t intercept or modify requests made using other libraries or methods like axios, fetch, or XMLHttpRequest.

Angular application with HTTP Interceptor

Let us create an angular application and use http interceptor to intercept the requests.

Step 1: Create an angular application

We can make use of the angular cli to create a new application using

ng new http-interceptor

Folder Structure

Dependencies

  "dependencies": {
    "@angular/animations": "^17.3.0",
    "@angular/common": "^17.3.0",
    "@angular/compiler": "^17.3.0",
    "@angular/core": "^17.3.0",
    "@angular/forms": "^17.3.0",
    "@angular/platform-browser": "^17.3.0",
    "@angular/platform-browser-dynamic": "^17.3.0",
    "@angular/router": "^17.3.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.3"
  },

Step 2: Make an HTTP request

It will create a simple http get request using the angular’s HttpClient so that we can intercept it and to use that we will need to import provideHttpClient in our app.config.ts.

HTML
<!-- app.component.html -->
<div>
    <input type="text" [(ngModel)]="name">
    <button (click)="sampleRequest()">Make Request</button>
    @if (response) {
    <div>
        <h2>Response</h2>
        <pre>{{ response | json }}</pre>
    </div>
    }
</div>
CSS
/* app.component.scss */

div {
    width: fit-content;
    margin: 8px auto;

    button {
        display: block;
        margin: 4px auto;
    }
}
JavaScript
// app.component.ts

import { JsonPipe } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterOutlet } from '@angular/router';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, FormsModule, JsonPipe],
    templateUrl: './app.component.html',
    styleUrl: './app.component.scss',
})
export class AppComponent {
    title = 'http-interceptor';
    name = 'John';
    response: any = null;

    constructor(private httpClient: HttpClient) {
        this.sampleRequest();
    }

    sampleRequest() {
        this.httpClient
            .get(`https://api.agify.io/?name=${this.name}`)
            .subscribe((data) => {
                this.response = data;
            });
    }
}
JavaScript
// app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
    providers: [provideRouter(routes), provideHttpClient()],
};

Once you have made this changes you will be able to make a get request on button click.

Step 3: To start the application run the following command

ng serve

Output

Step 4: Creating Http Interceptor

We can create an interceptor to log how much time the request takes to complete. We will create a file http.interceptor.ts in the app directory and update its content as below.

JavaScript
// http.interceptor.ts

import {
    HttpRequest,
    HttpResponse,
    HttpInterceptorFn,
    HttpHandlerFn,
} from '@angular/common/http';
import { tap } from 'rxjs/operators';

export const httpInterceptor: HttpInterceptorFn = (
    req: HttpRequest<unknown>,
    next: HttpHandlerFn
) => {
    const started = Date.now();

    return next(req).pipe(
        tap((event) => {
            if (event instanceof HttpResponse) {
                const elapsed = Date.now() - started;
                console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
            }
        })
    );
};
JavaScript
// app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { httpInterceptor } from './http.interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(routes),
        provideHttpClient(withInterceptors([httpInterceptor])),
    ],
};

Output