Angular's HttpClient returns RxJS Observables, making it easy to handle async data with operators like map, catchError, switchMap, and forkJoin.
// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([authInterceptor])),
],
};
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, forkJoin, throwError } from 'rxjs';
import { map, catchError, switchMap, tap, retry } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class DataService {
private http = inject(HttpClient);
// Transform response
getUserNames(): Observable<string[]> {
return this.http.get<User[]>('/api/users').pipe(
map(users => users.map(u => u.name)),
retry(2),
catchError(err => {
console.error(err);
return throwError(() => new Error('Failed to load users'));
})
);
}
// Chain requests
getUserWithPosts(userId: number): Observable<{ user: User; posts: Post[] }> {
return this.http.get<User>(`/api/users/${userId}`).pipe(
switchMap(user =>
this.http.get<Post[]>(`/api/users/${userId}/posts`).pipe(
map(posts => ({ user, posts }))
)
)
);
}
// Parallel requests
getDashboardData(): Observable<[User[], Post[], Comment[]]> {
return forkJoin([
this.http.get<User[]>('/api/users'),
this.http.get<Post[]>('/api/posts'),
this.http.get<Comment[]>('/api/comments'),
]);
}
}
// auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
if (token) {
req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
return next(req);
};