How does the HttpClient work in Angular and what are interceptors?
Learn how Angular's HttpClient makes Observable-based requests and how interceptors add auth tokens, logging, and error handling to every call in one place.
Expected Interview Answer
Angular's HttpClient is a service in @angular/common/http that performs HTTP requests and returns RxJS Observables, while interceptors are middleware that sit in the request/response pipeline to transform, inspect, or handle every call centrally.
HttpClient methods like get, post, and put return cold Observables, so a request only fires when you subscribe (or the async pipe subscribes for you), and it automatically parses JSON, supports typed responses, and exposes progress events. Interceptors implement HttpInterceptor (or, in modern Angular, are functional interceptors registered via withInterceptors) and form a chain: each can modify the outgoing HttpRequest, then pass it to next.handle and act on the returned response stream. Common uses are attaching auth tokens, adding headers, logging, caching, and centralized error handling and retries.
- Typed, Observable-based API with automatic JSON parsing
- Central place to attach auth tokens and headers
- Cross-cutting logging, caching and retry logic
- Uniform error handling for every request
- Testable via HttpClientTestingModule / provideHttpClientTesting
AI Mentor Explanation
HttpClient is the bowler delivering a ball (request) toward the boundary server and waiting for the return throw (response). An interceptor is the fielder standing in the path who can polish the ball, change its line, or catch a bad delivery before it ever reaches the batsman, applying the same treatment to every ball bowled that over.
Step-by-Step Explanation
Step 1
Provide HttpClient
Call provideHttpClient() in app config (or import HttpClientModule in older apps) so the service can be injected.
Step 2
Inject and call
Inject HttpClient and call typed methods like http.get<User[]>('/api/users'), which return an Observable.
Step 3
Subscribe or async pipe
Nothing fires until you subscribe, or the template async pipe subscribes on your behalf and manages cleanup.
Step 4
Register interceptors
Add functional interceptors via provideHttpClient(withInterceptors([authInterceptor])) or class-based ones through the HTTP_INTERCEPTORS token.
Step 5
Intercept and forward
In each interceptor, clone and modify the request, call next(req), and use RxJS operators like catchError or retry on the response stream.
What Interviewer Expects
- HttpClient returns cold Observables that need subscription
- Understanding of the interceptor chain and next.handle
- Immutability: requests are cloned, not mutated
- Real use cases like auth tokens and error handling
- Awareness of functional vs class-based interceptors
Common Mistakes
- Thinking the request fires without subscribing
- Mutating the HttpRequest directly instead of cloning it
- Forgetting to return next.handle(req) so the chain breaks
- Confusing HttpClient with the old Http/HttpModule API
- Not unsubscribing when subscribing manually instead of using async pipe
Best Answer (HR Friendly)
“HttpClient is Angular's built-in tool for talking to servers and getting data back. Interceptors are like checkpoints that automatically run for every request, so you can do things like add a login token or handle errors in one central place instead of repeating that code everywhere.”
Code Example
import { HttpInterceptorFn } 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 token = inject(AuthService).token;
const authReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
return next(authReq).pipe(
catchError((err) => {
console.error('HTTP error', err.status);
return throwError(() => err);
})
);
};
// app.config.ts
// provideHttpClient(withInterceptors([authInterceptor]))Follow-up Questions
- How do you clone a request to add a header without mutating the original?
- What is the difference between functional and class-based interceptors?
- How would you implement automatic retry with exponential backoff?
- How does the async pipe manage subscription and cleanup for HttpClient?
- How do you test code that uses HttpClient?
MCQ Practice
1. What does an HttpClient.get() call return?
HttpClient methods return cold Observables; the request only executes when something subscribes.
2. Why must an interceptor clone the request before modifying it?
HttpRequest objects are immutable, so you clone with modifications and pass the new request to next.
3. What happens if an interceptor never calls next(req)?
Each interceptor must forward to next to continue the chain; skipping it stops the request from proceeding.
Flash Cards
What does HttpClient return? — A cold RxJS Observable that only executes on subscription and auto-parses JSON.
What is an HTTP interceptor? — Middleware in the request/response pipeline that can modify requests, handle responses, and add cross-cutting logic.
How do you add a header in an interceptor? — Clone the request with req.clone({ setHeaders }) since HttpRequest is immutable.
How are functional interceptors registered? — Via provideHttpClient(withInterceptors([myInterceptor])) in the app config.
Common interceptor use cases? — Auth tokens, logging, caching, retries, and centralized error handling.