iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Angular interceptors let you apply shared behavior around HttpClient calls, such as adding authentication headers, logging, handling errors, or observing responses. For new applications, Angular recommends functional interceptors configured with provideHttpClient(withInterceptors([...])). Clone requests to change them, treat the result of next(req) as an event stream, and test each interceptor with Angular’s HTTP testing utilities.
How Angular interceptors work
An interceptor sits between an HttpClient call and the next handler in the HTTP pipeline. It can modify an outgoing request, inspect or transform the response event stream, or return a response without forwarding the request. Interceptors are useful for behavior shared across calls, including authentication, logging, caching, retries, error handling, timing, and loading indicators. Angular’s interceptor guide describes these capabilities and their configuration.
For a request sent through a chain, each interceptor passes a request to the next handler. The response events then travel back through the chain, allowing interceptors to observe or transform them.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsHow to register a functional interceptor
Functional interceptors are Angular’s recommended option for predictable behavior, particularly in complex configurations. Register them in the application providers with provideHttpClient(withInterceptors([...])). The order in the array is the order requests enter the interceptors.
#1 Best Overall
import { provideHttpClient, withInterceptors } from '@angular/common/http';
export const appConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor, loggingInterceptor])
),
],
};
Functional interceptors run in the injection context of the injector where they are registered, so they can use Angular’s inject() function to obtain services. See Angular’s HTTP setup guide for provider configuration details.
Add an authentication header safely
Use req.clone() to create a modified request. Request fields such as headers are immutable; header update methods return a new headers value rather than changing the existing one.
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getAuthToken();
const authenticatedReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`),
});
return next(authenticatedReq);
};
AuthService and the credential format are application-specific. Attach credentials only to the intended API destination; an interceptor should not send a secret indiscriminately to every URL used by the app. Angular’s example illustrates cloning and dependency injection, not an application’s security policy.
Free tools Windows power users keep installed
One-click scans. No signup required.
How to use class-based interceptors
Angular continues to support interceptors implemented as injectable classes that implement HttpInterceptor. Register the class with the HTTP_INTERCEPTORS multi-provider and enable DI-based interceptors using withInterceptorsFromDi().
Rank #2
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [
provideHttpClient(withInterceptorsFromDi()),
{
provide: HTTP_INTERCEPTORS,
useClass: LegacyAuthInterceptor,
multi: true,
},
]
This approach can suit an existing application already organized around injectable interceptor classes. Angular notes that ordering can be harder to predict in extensive or hierarchical dependency-injection configurations, which is why it recommends functional interceptors for new code. The API reference for withInterceptorsFromDi() documents this setup.
How to handle responses and errors
Recognize the final response event
next(req) returns an Observable of HttpEvent values, not just a response body. Depending on the request and configuration, the stream can include lifecycle events such as progress notifications as well as the completed response. Check event.type against HttpEventType.Response when logic should run only for the final response.
import { HttpEventType, HttpInterceptorFn } from '@angular/common/http';
import { tap } from 'rxjs';
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
tap(event => {
if (event.type === HttpEventType.Response) {
console.log(req.method, req.url, event.status);
}
})
);
};
For a caller that needs status, headers, and body, use observe: 'response' in the HttpClient request. By default, HttpClient returns the response body. Interceptors still work with the event stream around that request. Angular’s guide to making requests explains response observation and request options.
Recommended Free Tools
Handle failures in the Observable error channel
Network or connection failures, configured timeout failures, and unsuccessful backend responses are reported as HttpErrorResponse values through the Observable error channel. Network and timeout failures have status 0; a backend failure carries the status returned by the server and its error response. Use RxJS error-handling operators when an interceptor needs shared handling, and rethrow failures when callers must handle them themselves.
Rank #3
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
// Apply shared reporting or handling here.
return throwError(() => error);
})
);
};
Do not assume every failure is a server response: status 0 generally indicates a network or timeout problem, while a nonzero status identifies a backend response. See Angular’s request guide for the documented error behavior.
Immutability, retries, and interceptor metadata
HttpRequest and HttpResponse are mostly immutable, so create a clone to change request fields. Headers and parameters also use immutable update APIs: for example, set() replaces a value and append() adds one.
Request and response bodies are an exception: Angular does not deeply freeze them. Avoid mutating a body in place, especially if a request may be retried. An interceptor can execute again for a retry, and a prior in-place body change may therefore be applied or observed more than once.
For data intended only as interceptor metadata, use a typed HttpContextToken. Unlike most request fields, HttpContext is mutable, which allows state to be carried across retries. That mutable context is distinct from changing the request body.
Rank #4
When to return a synthetic response
An interceptor can return an HttpResponse directly instead of calling next(req), for example when serving a cached result. This short-circuits the chain: interceptors downstream from the one returning the synthetic response do not run, and the request does not reach the backend through that path. Consider which cross-cutting behaviors must still occur before using this pattern.
How to test an interceptor
Angular’s HTTP testing utilities let you issue a request, capture it, assert the interceptor’s changes, and simulate success or failure without sending a real network request. The official guide demonstrates testing one interceptor at a time.
-
Configure the test providers with
provideHttpClient(withInterceptors([interceptorUnderTest]))andprovideHttpClientTesting().What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy. -
Call the service or code that makes the HTTP request.
-
Use
HttpTestingControllerto capture the request and assert the relevant URL, headers, parameters, or other modified fields. -
Use the captured request’s
flush()method to supply a representative success response or backend error response. -
Use the testing controller’s network-error mechanism when testing connection failures, then verify there are no unexpected outstanding requests.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
For complete test setup and API details, see Angular’s HTTP testing guide.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

