HTTP Interceptors in Angular
What are Interceptors?
HTTP interceptors sit between your application and the server. They can inspect, transform, or handle every outgoing request and incoming response — perfect for adding auth tokens, logging, error handling, or loading indicators.
Angular 15+ supports functional interceptors — a simpler, more tree-shakeable alternative to class-based interceptors.
Auth Token Interceptor
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const token = auth.getToken();
if (token) {
const authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next(authReq);
}
return next(req);
};
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './auth.interceptor';
import { loggingInterceptor } from './logging.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor, loggingInterceptor])
)
]
};
Logging Interceptor
import { HttpInterceptorFn } from '@angular/common/http';
import { tap, finalize } from 'rxjs/operators';
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const start = Date.now();
console.log(`[HTTP] ${req.method} ${req.url}`);
return next(req).pipe(
tap({
next: response => console.log(`[HTTP] Response:`, response),
error: err => console.error(`[HTTP] Error:`, err)
}),
finalize(() => {
const duration = Date.now() - start;
console.log(`[HTTP] ${req.url} completed in ${duration}ms`);
})
);
};
Error Handling Interceptor
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';
import { NotificationService } from './notification.service';
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
const router = inject(Router);
const notify = inject(NotificationService);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
switch (error.status) {
case 401:
router.navigate(['/login']);
break;
case 403:
notify.error('You do not have permission.');
break;
case 404:
notify.error('Resource not found.');
break;
case 500:
notify.error('Server error. Please try again later.');
break;
default:
notify.error(`Unexpected error: ${error.message}`);
}
return throwError(() => error);
})
);
};
Loading Indicator Interceptor
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { LoadingService } from './loading.service';
export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
const loading = inject(LoadingService);
loading.show();
return next(req).pipe(
finalize(() => loading.hide())
);
};
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class LoadingService {
private _count = signal(0);
readonly isLoading = this._count.asReadonly();
show() { this._count.update(n => n + 1); }
hide() { this._count.update(n => Math.max(0, n - 1)); }
}