Angular HTTP Interceptors
HTTP interceptors are functions in the HttpClient pipeline that let you add headers, log, handle errors, and retry in one place.
HTTP Interceptors Essentials
- Inspect or transform requests and responses globally.
- An interceptor is a function in the HttpClient pipeline that runs for every request/response.
- Common uses: auth headers, error handling, retries, logging.
import { provideHttpClient, withInterceptors } from '@angular/common/http';
const authInterceptor = (req, next) => {
const cloned = req.clone({ setHeaders: { Authorization: 'Bearer token' } });
return next(cloned);
};
bootstrapApplication(App, {
providers: [provideHttpClient(withInterceptors([authInterceptor]))]
});
Notes:
- Related: See HttpClient and Services.
- Chain small, single-purpose interceptors.
- Handle errors centrally to keep components slim.
Writing an Interceptor
const logInterceptor = (req, next) => {
console.log(req.method, req.url);
return next(req);
};
provideHttpClient(withInterceptors([logInterceptor]));
import { HttpInterceptorFn } from '@angular/common/http';
const typedInterceptor: HttpInterceptorFn = (req, next) => {
// Example: add a header
return next(req.clone({ setHeaders: { 'X-Trace': 'demo' } }));
};
provideHttpClient(withInterceptors([typedInterceptor]));
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, inject } from '@angular/core';
import { HttpClient, provideHttpClient, withInterceptors, HttpResponse, HttpRequest, HttpHandlerFn } from '@angular/common/http';
import { of } from 'rxjs';
import { JsonPipe } from '@angular/common';
const logInterceptor = (req: HttpRequest<any>, next: HttpHandlerFn) => {
console.log('Request', req.method, req.url);
return next(req);
};
// Mock interceptor so the demo runs without external network
const mockInterceptor = (req: HttpRequest<any>, next: HttpHandlerFn) => {
if (req.method === 'GET' && req.url.includes('jsonplaceholder.typicode.com/todos/1')) {
const body = { id: 1, title: 'Mocked todo', completed: false };
return of(new HttpResponse({ status: 200, body }));
}
return next(req);
};
@Component({
selector: 'app-root',
standalone: true,
imports: [JsonPipe],
template: `
<h3>HTTP Interceptor</h3>
<button (click)="load()">Load</button>
<pre>{{ data | json }}</pre>
`
})
class App {
#http = inject(HttpClient);
data: any;
load() {
this.#http.get('https://jsonplaceholder.typicode.com/todos/1').subscribe(r => this.data = r);
}
}
bootstrapApplication(App, { providers: [provideHttpClient(withInterceptors([mockInterceptor, logInterceptor]))] });
<app-root></app-root>
Example explained
- withInterceptors([fn]): Registers one or more interceptor functions that run for every request and response.
- Interceptor fn (req, next): Optionally
req.clone(...)
to modify the request, then callnext(req)
to continue. - provideHttpClient(...): Enables HttpClient and composes interceptors at bootstrap.
Notes:
- Order: Requests flow in provided order; responses unwind in reverse.
- Immutable: Use
req.clone({...})
to modifyHttpRequest
. - Single-purpose: Compose focused interceptors with
withInterceptors([...])
.
Error Handling and Retries
- Centralize HTTP error handling in an interceptor.
- Keep components simpler by mapping errors to user-friendly messages.
- Add retry logic carefully with backoff; avoid retrying non‑retryable errors (e.g., 4xx).
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
const errorInterceptor = (req, next) => next(req).pipe(
catchError(err => {
// map/log/notify
return throwError(() => err);
})
);
provideHttpClient(withInterceptors([errorInterceptor]));
Guidelines:
- Use
catchError
in the interceptor to map errors to user-friendly messages. - Retry idempotent requests with
retry
or backoff usingretryWhen
; avoid retrying 4xx errors. - Handle auth (401) by refreshing tokens or redirecting; avoid doing navigation from many places-keep it centralized.
Ordering & Composition
- Compose small interceptors and mind the order they run in.
- Requests flow in the order provided; responses unwind in reverse.
const auth = (req, next) => next(req.clone({ setHeaders: { Authorization: '...' } }));
const log = (req, next) => { console.log(req.url); return next(req); };
// Request order: auth -> log
// Response order: log -> auth
provideHttpClient(withInterceptors([auth, log]));
Order: Requests flow through interceptors in the order provided; responses unwind in reverse.
Single-purpose: Keep interceptors focused (auth, logging, error handling) and compose with withInterceptors([...])
.
Stateless: Avoid storing mutable state inside interceptors; Use pure functions and injected services.
Retry safety: Retry only idempotent methods (e.g., GET/HEAD).
Avoid retrying non-idempotent methods unless you use idempotency keys.