> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP Interceptors

> Intercept and transform HTTP requests and responses with Angular interceptors.

## Overview

HTTP interceptors provide a mechanism to intercept and modify HTTP requests and responses globally. They act as middleware in the HTTP request pipeline, allowing you to implement cross-cutting concerns like authentication, logging, caching, and error handling.

## How interceptors work

Interceptors are called in the order they are provided and form a chain:

```
Request → Interceptor 1 → Interceptor 2 → Interceptor N → Backend → Server
                                                                        ↓
Response ← Interceptor 1 ← Interceptor 2 ← Interceptor N ← Backend ← Server
```

Each interceptor can:

* Inspect and modify outgoing requests
* Inspect and transform incoming responses
* Handle errors
* Retry requests
* Cache responses
* Block requests entirely

## Functional interceptors (recommended)

Angular provides `HttpInterceptorFn` for creating functional interceptors. This is the modern, recommended approach.

### Basic interceptor

```typescript auth.interceptor.ts theme={null}
import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  // Clone the request and add authorization header
  const authReq = req.clone({
    headers: req.headers.set('Authorization', 'Bearer my-token')
  });

  // Pass the cloned request to the next handler
  return next(authReq);
};
```

### Registering functional interceptors

Use `withInterceptors()` when providing `HttpClient`:

```typescript app.config.ts theme={null}
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';
import { loggingInterceptor } from './interceptors/logging.interceptor';

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

## Common use cases

### Authentication token

Add authentication tokens to all requests:

```typescript auth.interceptor.ts theme={null}
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';

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

  // Skip auth for login/register endpoints
  if (req.url.includes('/auth/')) {
    return next(req);
  }

  // Add token if available
  if (token) {
    const authReq = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${token}`)
    });
    return next(authReq);
  }

  return next(req);
};
```

### Logging

Log all HTTP requests and responses:

```typescript logging.interceptor.ts theme={null}
import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { tap } from 'rxjs/operators';

export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  const started = Date.now();
  console.log(`HTTP ${req.method} ${req.url} - Started`);

  return next(req).pipe(
    tap({
      next: (event) => {
        if (event instanceof HttpResponse) {
          const elapsed = Date.now() - started;
          console.log(`HTTP ${req.method} ${req.url} - Success (${elapsed}ms)`);
          console.log('Response:', event.body);
        }
      },
      error: (error) => {
        const elapsed = Date.now() - started;
        console.error(`HTTP ${req.method} ${req.url} - Failed (${elapsed}ms)`);
        console.error('Error:', error);
      }
    })
  );
};
```

### Loading indicator

Show a loading spinner during HTTP requests:

```typescript loading.interceptor.ts theme={null}
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { LoadingService } from '../services/loading.service';

export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
  const loadingService = inject(LoadingService);
  
  loadingService.show();

  return next(req).pipe(
    finalize(() => loadingService.hide())
  );
};
```

```typescript loading.service.ts theme={null}
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class LoadingService {
  private loadingSubject = new BehaviorSubject<boolean>(false);
  loading$ = this.loadingSubject.asObservable();

  show() {
    this.loadingSubject.next(true);
  }

  hide() {
    this.loadingSubject.next(false);
  }
}
```

### Caching

Cache GET requests:

```typescript cache.interceptor.ts theme={null}
import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { CacheService } from '../services/cache.service';

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

  const cacheService = inject(CacheService);
  const cachedResponse = cacheService.get(req.url);

  // Return cached response if available
  if (cachedResponse) {
    console.log(`Returning cached response for ${req.url}`);
    return of(cachedResponse);
  }

  // Otherwise, make the request and cache the response
  return next(req).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        cacheService.set(req.url, event);
      }
    })
  );
};
```

### API prefix

Add base URL to all requests:

```typescript api-prefix.interceptor.ts theme={null}
import { HttpInterceptorFn } from '@angular/common/http';
import { environment } from '../environments/environment';

export const apiPrefixInterceptor: HttpInterceptorFn = (req, next) => {
  // Skip if already absolute URL
  if (req.url.startsWith('http://') || req.url.startsWith('https://')) {
    return next(req);
  }

  // Add API base URL
  const apiReq = req.clone({
    url: `${environment.apiUrl}${req.url}`
  });

  return next(apiReq);
};
```

### Retry logic

Automatically retry failed requests:

```typescript retry.interceptor.ts theme={null}
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { retry, timer } from 'rxjs';
import { catchError } from 'rxjs/operators';

export const retryInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    retry({
      count: 3,
      delay: (error, retryCount) => {
        // Only retry on server errors (5xx)
        if (error instanceof HttpErrorResponse && error.status >= 500) {
          console.log(`Retry attempt ${retryCount} for ${req.url}`);
          // Exponential backoff: 1s, 2s, 4s
          return timer(1000 * Math.pow(2, retryCount - 1));
        }
        // Don't retry for client errors
        throw error;
      }
    })
  );
};
```

## Class-based interceptors (legacy)

The older class-based approach using `HttpInterceptor` interface is still supported but not recommended for new code.

### Creating a class-based interceptor

```typescript auth.interceptor.ts theme={null}
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({
      headers: req.headers.set('Authorization', 'Bearer my-token')
    });

    return next.handle(authReq);
  }
}
```

### Registering class-based interceptors

```typescript app.config.ts theme={null}
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withInterceptorsFromDi()),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
};
```

## Modifying requests

Requests are immutable, so you must clone them to make changes:

```typescript theme={null}
export const modifyRequestInterceptor: HttpInterceptorFn = (req, next) => {
  // Clone and modify URL
  const modifiedReq = req.clone({
    url: req.url.replace('/api/', '/api/v2/')
  });

  // Clone and add headers
  const withHeaders = req.clone({
    headers: req.headers
      .set('X-Custom-Header', 'value')
      .set('Accept', 'application/json')
  });

  // Clone and add query parameters
  const withParams = req.clone({
    params: req.params
      .set('timestamp', Date.now().toString())
      .set('version', '2.0')
  });

  // Clone and modify body
  const withBody = req.clone({
    body: { ...req.body, timestamp: Date.now() }
  });

  return next(withHeaders);
};
```

## Modifying responses

Transform response data:

```typescript transform-response.interceptor.ts theme={null}
import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { map } from 'rxjs/operators';

export const transformResponseInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    map(event => {
      if (event instanceof HttpResponse) {
        // Transform the response body
        const transformedBody = {
          data: event.body,
          timestamp: Date.now(),
          url: req.url
        };

        // Return a new response with transformed body
        return event.clone({
          body: transformedBody
        });
      }
      return event;
    })
  );
};
```

## Conditional interception

Apply interceptor logic conditionally:

```typescript conditional.interceptor.ts theme={null}
import { HttpInterceptorFn, HttpContext, HttpContextToken } from '@angular/common/http';

// Create a context token
export const SKIP_AUTH = new HttpContextToken<boolean>(() => false);

export const conditionalAuthInterceptor: HttpInterceptorFn = (req, next) => {
  // Check context
  if (req.context.get(SKIP_AUTH)) {
    return next(req);
  }

  // Apply auth logic
  const authReq = req.clone({
    headers: req.headers.set('Authorization', 'Bearer token')
  });

  return next(authReq);
};
```

Use context when making requests:

```typescript theme={null}
import { HttpContext } from '@angular/common/http';
import { SKIP_AUTH } from './interceptors/conditional.interceptor';

// Skip authentication for this request
http.get('/public/data', {
  context: new HttpContext().set(SKIP_AUTH, true)
}).subscribe();
```

## Interceptor order

Interceptors execute in the order they are provided:

```typescript app.config.ts theme={null}
provideHttpClient(
  withInterceptors([
    apiPrefixInterceptor,    // 1. Add base URL
    authInterceptor,         // 2. Add auth token
    loggingInterceptor,      // 3. Log request
    cacheInterceptor,        // 4. Check cache
    retryInterceptor         // 5. Retry on failure
  ])
)
```

On the response path, they execute in reverse order:

```
Request:  apiPrefix → auth → logging → cache → retry → backend
Response: backend → retry → cache → logging → auth → apiPrefix
```

## Accessing dependencies

Use `inject()` to access services in functional interceptors:

```typescript theme={null}
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';

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

  const token = authService.getToken();

  if (!token && !req.url.includes('/public')) {
    router.navigate(['/login']);
    throw new Error('Not authenticated');
  }

  // Continue with request
  return next(req);
};
```

## Testing interceptors

```typescript auth.interceptor.spec.ts theme={null}
import { TestBed } from '@angular/core/testing';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './auth.interceptor';

describe('AuthInterceptor', () => {
  let httpClient: HttpClient;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting()
      ]
    });

    httpClient = TestBed.inject(HttpClient);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should add Authorization header', () => {
    httpClient.get('/api/users').subscribe();

    const req = httpMock.expectOne('/api/users');
    expect(req.request.headers.has('Authorization')).toBe(true);
    expect(req.request.headers.get('Authorization')).toContain('Bearer');

    req.flush([]);
  });
});
```

## Best practices

<Tip>
  **Use functional interceptors**

  Prefer `HttpInterceptorFn` over class-based interceptors for cleaner, more composable code.
</Tip>

<Tip>
  **Keep interceptors focused**

  Each interceptor should have a single responsibility. Create multiple small interceptors instead of one large one.
</Tip>

<Warning>
  Always clone requests before modifying them. Requests are immutable by design.
</Warning>

<Tip>
  **Use context for conditional logic**

  Use `HttpContext` to pass metadata to interceptors instead of checking URLs or headers.
</Tip>

<Tip>
  **Handle errors gracefully**

  Interceptors should not crash. Always handle errors and decide whether to propagate them or recover.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Error handling" icon="triangle-exclamation" href="/http/error-handling">
    Learn how to handle HTTP errors effectively.
  </Card>

  <Card title="HttpClient" icon="globe" href="/http/http-client">
    Back to HttpClient documentation.
  </Card>
</CardGroup>
