Angular Interceptors: A Quick Guide

Angular Interceptors: A Quick Guide

Ever had your app ask for something from the server, and it's met with a big, "nope" in the form of an error? Frustrating, right?
I found myself in this very situation while working on the development of the frontend in Angular and came across HTTP error 400. In the realm of HTTP/1.1 standards, this error, known as the "Bad Request" response, surfaces when the server is unable or unwilling to process a request due to malformed client input, invalid parameters, or other issues.
The legacy code was only handling the response; since this request did not return a response, the request failed. So, what would be the best way to handle this? Angular interceptors!
Angular interceptors are more than just handling errors. They can also be used to inject authorization tokens in HTTP requests. This gave me the idea to write my first blog on Hashnode. So here I am, with a quick guide to Angular interceptors.

Introduction

Interceptors in Angular are services that can be used to intercept HTTP requests and responses. They provide a way to modify or handle requests and responses globally before they reach the server or the application.

Create an Interceptor:

// Import necessary modules and classes
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

// Decorate the class as an injectable service
@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  // Implement the intercept method required by the HttpInterceptor interface
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    // Hardcoded authentication token for demonstration purposes
    const authToken = 'your-auth-token';

    // Clone the original request and add the authorization header
    const authReq = req.clone({
      setHeaders: {
        Authorization: `Bearer ${authToken}`
      }
    });

    // Pass the cloned request to the next handler in the chain
    return next.handle(authReq).pipe(
        // Map the response to itself (optional step)
        map((response) => response),
        // Catch and handle any errors
        catchError(this.handleError)
    );
  }

  // Optional: Define a private method to handle errors
  private handleError = (error) => {
    // Implement error handling logic here
    console.error('Error:', error);
    // Optionally, throw the error again or handle it gracefully
    throw error;
  }
}

Explanation:

  • AuthInterceptor is a class that implements the HttpInterceptor interface. This interface allows you to intercept and transform HTTP requests or responses.

  • The intercept method is the core of the interceptor. It takes the original HttpRequest and a HttpHandler. The HttpHandler is responsible for passing the request along the chain of interceptors and ultimately to the server.

  • In this interceptor, a token (authToken) is hardcoded for demonstration purposes. In a real-world scenario, you would retrieve the token dynamically, perhaps from a service or another source.

  • The req.clone() method is used to create a new request based on the original request (req). This new request includes an additional header (Authorization) with the token.

  • The cloned request (authReq) is then passed to the next handler in the chain using return next.handle(authReq);. The pipe operator allows you to chain RxJS operators. In this case, map is used to map the response to itself (which is optional in this context).

  • catchError is used to catch and handle any errors that might occur during the request.

  • The interceptor includes an optional private method (handleError) to handle errors. This method is called within the catchError operator.

Register the Interceptor

// app.module.ts

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  declarations: [/* your components */],
  imports: [HttpClientModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ],
  bootstrap: [/* your root component */]
})
export class AppModule { }

Explanation:

  • In your app.module.ts file, you import the necessary modules and the AuthInterceptor class.

  • The HTTP_INTERCEPTORS token is used to provide a list of interceptors. In this case, you provide the AuthInterceptor using the useClass property.

  • The multi: true option indicates that you're providing multiple interceptors.

In conclusion, Angular interceptors serve as essential tools in optimizing your application's interaction with the server. As you navigate your Angular projects, consider interceptors as reliable companions, adept at simplifying common challenges.