Skip to content
intermediate Phase 9 · HTTP Client and Interceptors

HTTP Interceptors

Wrap every request to add auth headers, logging, or retries.

45m
0 problems
Topic Progress 0%

Creating Interceptors

HTTP interceptors wrap every outgoing request and incoming response, enabling cross-cutting concerns like auth, logging, and error handling.

Functional Interceptor

import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
import { catchError, throwError } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const authService = inject(AuthService);
  const token = authService.getToken();

  if (token) {
    req = req.clone({
      setHeaders: { Authorization: `Bearer ${token}` },
    });
  }

  return next(req);
};

Registering Interceptors

import { provideHttpClient, withInterceptors } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([authInterceptor, loggingInterceptor, errorInterceptor])
    ),
  ],
};

Interceptor Execution Order

Interceptors run in registration order:

Request → authInterceptor → loggingInterceptor → errorInterceptor → Server
Response → errorInterceptor → loggingInterceptor → authInterceptor → Subscriber

Modifying Requests

export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  const started = Date.now();

  console.log(`[${req.method}] ${req.url}`);

  return next(req).pipe(
    tap({
      next: () => {
        const duration = Date.now() - started;
        console.log(`[${req.method}] ${req.url} — ${duration}ms`);
      },
    })
  );
};

Error Handling Interceptor

Interceptors can observe responses to handle errors globally.

Global Error Interceptor

import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { catchError, throwError } from 'rxjs';

export const errorInterceptor: HttpInterceptorFn = (req, next) => {
  const router = inject(Router);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401) {
        router.navigate(['/login']);
      }

      if (error.status === 500) {
        console.error('Server error:', error);
      }

      return throwError(() => error);
    })
  );
};

Retry Interceptor

import { HttpInterceptorFn } from '@angular/common/http';
import { retry } from 'rxjs';

export const retryInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    retry({
      count: 3,
      delay: 1000, // Wait 1 second between retries
    })
  );
};

Caching Interceptor

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

const cache = new Map<string, HttpResponse<any>>();

export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
  if (req.method !== 'GET') {
    return next(req);
  }

  const cached = cache.get(req.url);
  if (cached) {
    return of(cached.clone());
  }

  return next(req).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        cache.set(req.url, event.clone());
      }
    })
  );
};

Common Interceptor Use Cases

Use Case Interceptor
Auth headers Clone request with Authorization header
Logging Log request method, URL, and duration
Error handling Catch and handle specific status codes
Retry Resubscribe on transient failures
Caching Return cached GET responses
Loading state Show/hide loading indicator

Quiz

1. What is the modern functional interceptor syntax?

Question 1 options

2. In what order do interceptors run?

Question 2 options

3. How do you modify a request in an interceptor?

Question 3 options

Flashcards

Question

What is a functional interceptor?

Answer

HttpInterceptorFn — (req, next) => Observable. Uses inject() for dependencies.

Question

How do interceptors run?

Answer

Registration order for requests, reverse for responses. Forms a pipeline.

Question

How do you modify a request?

Answer

req.clone({ setHeaders: {...} }) — requests are immutable.

Revision Notes

Key Takeaways

  • 1. HttpInterceptorFn is the modern functional approach
  • 2. Interceptors run in registration order for requests
  • 3. req.clone() creates modified request copies
  • 4. Common uses: auth headers, logging, error handling, caching

Interview Tips

  • • Explain the interceptor pipeline
  • • Be ready to describe common interceptor use cases
  • • Mention that requests are immutable — always clone

Cheat Sheet

HTTP Interceptors Cheat Sheet

Functional interceptor:

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const token = inject(AuthService).getToken();
  if (token) {
    req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
  }
  return next(req);
};

Register:

provideHttpClient(withInterceptors([authInterceptor, loggingInterceptor]))

Modify request:

req.clone({ setHeaders: {...}, setParams: {...} })

Continue Learning