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?
2. In what order do interceptors run?
3. How do you modify a request in an interceptor?
Flashcards
Question
What is a functional interceptor?
Click to reveal answer
Answer
HttpInterceptorFn — (req, next) => Observable. Uses inject() for dependencies.
Question
How do interceptors run?
Click to reveal answer
Answer
Registration order for requests, reverse for responses. Forms a pipeline.
Question
How do you modify a request?
Click to reveal answer
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: {...} })