Understanding Angular Interceptors: Beyond HTTP

Understand how to create and use Angular HTTP interceptors for authentication, error handling, and caching. Use Requestly to test, modify, and debug HTTP requests in real time for more reliable Angular app behavior.

Get Started free
Home Guide Understanding Angular Interceptors: Beyond HTTP

Understanding Angular Interceptors: Beyond HTTP

Angular HTTP interceptors are fundamental to managing HTTP requests and responses in Angular applications. They let you centralize logic for modifying requests, handling errors, adding authentication, logging activity, and optimizing performance. Understanding interceptors helps you write cleaner and more maintainable code.

This article explains what Angular HTTP interceptors are, how they work, and how to use them effectively.

What is an Angular HTTP Interceptor?

An Angular HTTP interceptor is a special class that sits between your app and the server. It watches every HTTP request your app sends and every response it gets back. This lets you check or change requests and responses before your app uses them.

Interceptors help you add login tokens, handle errors, or log information all in one place. Instead of repeating the same code everywhere, you write it once inside the interceptor. This makes your app easier to manage and keeps your main code cleaner.

In simple terms, an Angular HTTP interceptor lets you control how your app talks to servers by catching and changing data on the way out or back.

Key Responsibilities of Angular HTTP Interceptors

Interceptors handle several key tasks in an Angular app. Each responsibility ensures your HTTP communication is secure, efficient, and easy to debug.

1. Modifying HTTP Requests in Angular Interceptors

Interceptors can change outgoing requests before they are sent to the server. Typical modifications include:

  • Adding authorization tokens or API keys in headers
  • Setting custom headers required by the backend
  • Changing request URLs or parameters dynamically
  • Adding content types or accept headers

Below is a code snippet that adds an authorization header:

import { Injectable } from '@angular/core';

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';

import { Observable } from 'rxjs';



@Injectable()

export class AuthInterceptor implements HttpInterceptor {

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

    const authReq = req.clone({

      setHeaders: { Authorization: `Bearer YOUR_TOKEN_HERE` }

    });

    return next.handle(authReq);

  }

}

This interceptor clones the request and adds an Authorization header without altering the original request object.

2. Modifying HTTP Responses in Angular Interceptors

Interceptors can also inspect and modify responses from the server before they reach your components or services. This is useful for:

  • Adding caching layers
  • Transforming response data to match your frontend models
  • Logging response data for debugging
  • Handling global response formatting

Here’s how you can modify a response body:

import { map } from 'rxjs/operators';



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

  return next.handle(req).pipe(

    map(event => {

      // Assuming event is an HttpResponse

      if (event.type === HttpEventType.Response) {

        const modifiedBody = { ...event.body, extraData: 'Added by interceptor' };

        return event.clone({ body: modifiedBody });

      }

      return event;

    })

  );

}

3. Error Handling with Angular HTTP Interceptors

Angular HTTP interceptors provide a centralized way to catch and manage HTTP errors. Instead of handling errors in every service or component, you can handle them globally. For example, you can detect a 401 Unauthorized error and redirect the user to the login page, or handle server errors by showing a friendly message.

Here is an example showing how to catch HTTP errors using RxJS catchError:

import { catchError } from 'rxjs/operators';

import { throwError } from 'rxjs';

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



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

  return next.handle(req).pipe(

    catchError((error: HttpErrorResponse) => {

      if (error.status === 401) {

        // Example: Redirect user to login page on 401 Unauthorized

        // You can inject a router and call router.navigate(['/login']);

        console.log('Unauthorized access - redirecting to login');

      }

      if (error.status === 500) {

        // Example: Show a global server error notification

        console.log('Server error occurred');

      }

      // Pass the error along so other parts of your app can handle it if needed

      return throwError(error);

    })

  );

}

In this example, the interceptor listens for errors on every HTTP response. It executes custom logic if it finds a specific status code, like 401 or 500. The error is then re-thrown to allow other parts of the app to react if necessary.

4. Authentication and Authorization Using Angular HTTP Interceptors

Interceptors are commonly used to add authentication tokens to outgoing HTTP requests. This ensures all requests carry the necessary credentials without manually adding tokens in every service.

Here is how you can add a JWT token to request headers:

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

  const token = this.authService.getToken(); // Get token from a service

  if (token) {

    // Clone the request and add the Authorization header

    const authReq = req.clone({

      headers: req.headers.set('Authorization', `Bearer ${token}`)

    });

    return next.handle(authReq);

  }

  

  // If no token, proceed without modifying the request

  return next.handle(req);

}

In this example, the interceptor retrieves the token from a service. If the token exists, it clones the HTTP request and adds an Authorization header with the token. Then, it forwards the modified request.

You can extend this logic to:

  • Check if the token has expired before sending requests
  • Refresh the token silently in the background
  • Block requests and redirect users to login if no valid token exists

5. Logging and Monitoring in Angular HTTP Interceptors

Logging HTTP requests and responses helps monitor API usage and debug problems. You can track how long each request takes and log key response information.

Here’s a logging interceptor example:

import { tap } from 'rxjs/operators';

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



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

  const startTime = Date.now();



  return next.handle(req).pipe(

    tap(event => {

      if (event.type === HttpEventType.Response) {

        const elapsedTime = Date.now() - startTime;

        console.log(`Request to ${req.urlWithParams} took ${elapsedTime} ms and returned status ${event.status}.`);

      }

    })

  );

}

In this example, the interceptor records the start time before sending the request. When the response arrives, it calculates the total time and logs it with the URL and status code.

6. Performance Optimizations with Angular HTTP Interceptors

Interceptors can help improve your app’s performance by controlling network traffic and reducing unnecessary requests.

Here are some common techniques:

  • Adding Caching Headers: Add headers like Cache-Control or If-None-Match to help the browser or server cache responses.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

  const cachedReq = req.clone({

    headers: req.headers.set('Cache-Control', 'max-age=600')

  });

  return next.handle(cachedReq);

}
  • Skipping Duplicate Requests: Prevent sending the same request multiple times simultaneously by tracking ongoing requests and cancelling duplicates.
  • Debouncing Requests: Delay requests until the user stops typing or performing actions to avoid excessive calls (often combined with RxJS operators outside interceptors).

HTTP Interceptor Banner

How to Set Up an Angular HTTP Interceptor

Creating and configuring an Angular HTTP interceptor involves three main steps.

1. Generate an Angular Interceptor 

Use Angular CLI to generate the interceptor class:

ng generate interceptor auth

This creates a new auth.interceptor.ts file with boilerplate code.

2. Implement an Angular HTTP Interceptor

Implement the HttpInterceptor interface by adding the intercept method:

export class AuthInterceptor implements HttpInterceptor {

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

    // modify request here

    return next.handle(req);

  }

}

3. Provide the Angular HTTP Interceptor Globally

Add the interceptor to your app module providers:

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



providers: [

  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }

]

The multi: true flag allows multiple interceptors to be registered.

Using Multiple Angular HTTP Interceptors

Angular allows you to use more than one HTTP interceptor in your app. This is helpful because different interceptors can handle different tasks. For example, you might have one interceptor to add authentication tokens and another to log requests.

When you have multiple interceptors, Angular will execute them in the order they are listed in the providers array. Each interceptor receives the request, does its job, and then calls next.handle(req) to pass the request to the next interceptor in line.

Here’s how to register multiple interceptors:

providers: [

  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },

  { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }

]

In this example:

  • AuthInterceptor adds authentication headers to each request.
  • LoggingInterceptor logs details of each request and response.

Since order matters, you should register interceptors like AuthInterceptor first. This ensures that your logging interceptor captures the final form of the request or response, after the authentication headers are added.

How the Execution Order of Angular HTTP Interceptors Works

When Angular sends an HTTP request, it runs interceptors in the order you register them. However, it runs interceptors in the opposite order when it receives a response.

For example, if you register interceptors A, B, and C in that sequence:

  • The outgoing request flow goes through A, B, and C.
  • Incoming response flow goes through C, then B, then A.

Understanding this order is essential because interceptors may rely on changes made by others. For instance, an authentication interceptor should run before a logging interceptor to ensure the logged request includes authentication details.

Real-World Use Cases of Angular HTTP Interceptors

Angular HTTP interceptors solve many common problems by handling repeated tasks in one place. Below are key examples showing how interceptors improve Angular apps.

1. Adding Authentication Tokens Using Angular Interceptors

APIs often require a token, such as a JWT, to prove who is making the request. Without interceptors, you must manually add this token to every HTTP request, which is repetitive and error-prone. An interceptor automatically adds the token to the headers of each request before it is sent. This ensures all requests are authenticated and keeps your code cleaner and more secure.

2. Global Error Handling with Angular HTTP Interceptors

Managing errors in every HTTP call inside components or services causes repeated code and inconsistent handling. Interceptors catch errors like 401 Unauthorized or 500 Server Errors centrally. They can then redirect users to login, show messages, or log errors. This creates a consistent error response across the entire app, reducing duplicated code.

3. Request and Response Transformation in Angular HTTP Interceptors

Sometimes your backend expects requests in a specific format or sends extra data you don’t need. Interceptors can change the outgoing request data to fit the backend format or remove unnecessary information from responses before your app uses it. This keeps your components focused on the main business logic without worrying about data details.

4. Caching and Performance with Angular HTTP Interceptors

Fetching the same data repeatedly wastes time and bandwidth. Interceptors can store responses from GET requests and serve the cached data for repeated calls. This reduces server load and speeds up your app. You can add logic in interceptors to decide when to refresh the cache to keep data updated while improving performance.

How to Test Angular HTTP Interceptors?

Testing Angular HTTP interceptors ensures they work correctly and handle requests and responses as expected. You can test interceptors using Angular’s testing utilities and HttpClientTestingModule. Here’s how.

1. Set Up the Test Environment

Import HttpClientTestingModule and configure the testing module to include your interceptor.

import { TestBed } from '@angular/core/testing';

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';

import { AuthInterceptor } from './auth.interceptor';



describe('AuthInterceptor', () => {

  let httpMock: HttpTestingController;

  let httpClient: HttpClient;



  beforeEach(() => {

    TestBed.configureTestingModule({

      imports: [HttpClientTestingModule],

      providers: [

        { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }

      ]

    });



    httpMock = TestBed.inject(HttpTestingController);

    httpClient = TestBed.inject(HttpClient);

  });



  afterEach(() => {

    httpMock.verify(); // Verify no outstanding requests

  });

});

This code sets up a test module with HttpClientTestingModule and registers your interceptor globally.

2. Test Request Modification

Check if the interceptor correctly modifies the outgoing request, such as adding an authorization header.

it('should add an Authorization header', () => {

  httpClient.get('/test').subscribe(response => {

    expect(response).toBeTruthy();

  });



  const httpRequest = httpMock.expectOne('/test');



  expect(httpRequest.request.headers.has('Authorization')).toBeTrue();

  expect(httpRequest.request.headers.get('Authorization')).toBe('Bearer fake-token');



  httpRequest.flush({}); // mock empty response

});

This test sends a GET request and verifies that the interceptor added the expected header.

3. Test Response Handling and Error Catching

You can also test how the interceptor handles HTTP errors.

it('should handle 401 errors by redirecting', () => {

  spyOn(window.location, 'assign'); // Spy on redirect



  httpClient.get('/test').subscribe({

    error: error => {

      expect(error.status).toBe(401);

      expect(window.location.assign).toHaveBeenCalledWith('/login');

    }

  });



  const httpRequest = httpMock.expectOne('/test');

  httpRequest.flush({}, { status: 401, statusText: 'Unauthorized' });

});

This test simulates a 401 Unauthorized error and checks if the interceptor redirects to the login page.

Why Use External HTTP Interception Tools like Requestly

External tools like Requestly add powerful features for controlling and testing HTTP requests outside your Angular app code. They work in your browser and let you modify requests and responses on the fly without changing your app. Here are the key features of Requestly:

  • Request Modification: Requestly enables you to change request URLs, headers, or bodies before they leave the browser. You can test different backend scenarios or simulate missing parameters easily.
  • Response Mocking: You can replace server responses with custom data. This is useful when the backend is incomplete or unstable, letting you develop frontend features without waiting for APIs.
  • Rule-based Interception: Requestly uses rules to decide which requests to modify or block based on URL patterns, headers, or methods. This lets you precisely target specific API calls.
  • Simulating Network Conditions: You can delay responses or throttle bandwidth to see how your app behaves on slow or unstable connections. This helps improve error handling and UI feedback.
  • Testing Error Responses: You can force your app to receive HTTP errors like 404 or 500. This helps test how your app handles failures without breaking the backend.
  • No Code Changes Needed: Since Requestly offers a browser extension, you do not have to change your Angular app code. This makes it faster to test various scenarios and debug issues.

Talk to an Expert

Common Pitfalls of Angular HTTP Interceptors

Developers often face these common issues when working with Angular HTTP interceptors.

  • Forgetting to call next.handle(): Every interceptor must call next.handle(req) to pass the request along. Missing this stops all HTTP requests and causes the app to freeze.
  • Incorrect order of interceptors: Interceptor registration order matters. For example, authentication interceptors should run before logging interceptors to ensure logs capture final request details.
  • Modifying requests without cloning: HTTP requests are immutable. You must clone the request with req.clone() before making changes. Skipping cloning causes errors or unexpected behavior.
  • Handling errors without rethrowing: When catching errors, rethrow them using throwError() so the app can handle them properly. Swallowing errors lead to silent failures.
  • Overloading interceptors with too many responsibilities: Keep interceptors focused on one task, like adding headers or error handling. Combining multiple tasks makes maintenance and testing difficult.
  • Creating infinite loops in requests: Modifying and resending requests without proper checks can cause interceptors to loop endlessly, crashing the app.
  • Not disabling interceptors during testing: Interceptors may alter requests unexpectedly in tests. Disable or mock them when they are not part of the test to avoid failures.

Best Practices for Angular HTTP Interceptors

Here’s how you can use Angular HTTP interceptors more effectively and keep your app code clean and efficient.

  • Keep Interceptors Focused: Each interceptor should handle one concern, like adding authentication tokens or logging responses. This makes them easier to manage and debug.
  • Use Multi:True: When registering interceptors, always use multi: true in the providers array. This ensures Angular can chain multiple interceptors without replacing existing ones.
  • Keep the Order Logical: Register interceptors in a way that makes sense for your app’s needs. For example, authentication tokens should be added before logging request data.
  • Avoid Blocking Calls: Avoid using synchronous or blocking operations in interceptors because they can delay requests and slow down the app.
  • Test Thoroughly: Write unit tests to check how your interceptors behave in different scenarios. This helps catch bugs early and ensures interceptors work together properly.
  • Handle Errors Consistently: Use a consistent approach to handle interceptor errors, whether you redirect users, show error messages, or log issues. This keeps your app’s behavior uniform and predictable.

Conclusion

Angular HTTP interceptors play a key role in managing HTTP communication by handling repetitive tasks like authentication, logging, and error handling globally. They help keep component and service code clean and consistent. However, they require careful implementation and testing to avoid common pitfalls and ensure smooth communication with backend services.

Requestly provides a simple way to modify and test HTTP traffic without changing your application code. It helps developers mock APIs, rewrite URLs, and debug network requests in real time, making it easier to build and troubleshoot HTTP interceptors.

Try Requestly For Free

Tags
Automation Testing Types of Testing UI Testing

Get answers on our Discord Community

Join our Discord community to connect with others! Get your questions answered and stay informed.

Join Discord Community
Discord