Welcome to our deep dive into Angular’s HttpClient! This powerful module enables seamless communication with backend services and APIs. Let’s master it together.
Why HttpClient?
Angular’s HttpClient provides:
- A testable, type-safe way to make HTTP requests
- Observable-based API
- Interceptor support
- Request/response transformation
- Error handling built-in
Setting Up HttpClient
First, import HttpClientModule
in your root module:
typescript
// app.module.ts import { HttpClientModule } from '@angular/common/http'; @NgModule({ imports: [ HttpClientModule // ← Add this ] }) export class AppModule { }
Basic HTTP Operations
Here’s how to perform CRUD operations:
1. GET Request (Fetch Data)
typescript
// data.service.ts getPosts(): Observable<Post[]> { return this.http.get<Post[]>('https://api.example.com/posts'); }
2. POST Request (Create Data)
typescript
createPost(post: Post): Observable<Post> { return this.http.post<Post>('https://api.example.com/posts', post); }
3. PUT/PATCH Request (Update Data)
typescript
updatePost(id: number, post: Post): Observable<Post> { return this.http.put<Post>(`https://api.example.com/posts/${id}`, post); // Or for partial updates: // return this.http.patch<Post>(...) }
4. DELETE Request (Remove Data)
typescript
deletePost(id: number): Observable<void> { return this.http.delete<void>(`https://api.example.com/posts/${id}`); }
Advanced Request Configuration
Customize requests with options:
typescript
getPosts(): Observable<Post[]> { return this.http.get<Post[]>('https://api.example.com/posts', { params: new HttpParams().set('limit', '10'), // Query params headers: new HttpHeaders().set('Authorization', 'Bearer token'), observe: 'response' // Get full HttpResponse }); }
Handling Responses
Process responses properly:
typescript
getPosts(): Observable<Post[]> { return this.http.get<Post[]>('/api/posts').pipe( tap(response => console.log('Raw response:', response)), map(response => response.map(post => ({ ...post, createdAt: new Date(post.createdAt) }))), catchError(this.handleError) ); } private handleError(error: HttpErrorResponse) { if (error.status === 0) { console.error('Client-side or network error:', error.error); } else { console.error(`Backend returned code ${error.status}:`, error.error); } return throwError(() => new Error('Something went wrong')); }
Interceptors: Powerful Middleware
Create interceptors for cross-cutting concerns:
typescript
// auth.interceptor.ts @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const authToken = localStorage.getItem('auth_token'); if (authToken) { const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${authToken}`) }); return next.handle(authReq); } return next.handle(req); } }
Register interceptors in your module:
typescript
providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ]
Common Interceptor Use Cases
- Authentication: Add auth tokens
- Error Handling: Global error processing
- Caching: Cache responses
- Logging: Track requests
- Mocking: Fake backend for development
Progress Events
Track upload/download progress:
typescript
uploadFile(file: File): Observable<HttpEvent<any>> { const formData = new FormData(); formData.append('file', file); return this.http.post('/api/upload', formData, { reportProgress: true, observe: 'events' }).pipe( filter(event => event.type === HttpEventType.UploadProgress), map(event => { if (event.type === HttpEventType.UploadProgress) { return Math.round(100 * event.loaded / (event.total || 1)); } return 0; }) ); }
Best Practices
- Service Layer: Keep HTTP calls in services, not components
- Type Safety: Always type your requests/responses
- Error Handling: Handle errors at service level
- Unsubscribe: Manage subscriptions properly
- Environment Config: Store API URLs in environment files
Hands-On Exercise
Build a complete UserService
with:
- Methods for all CRUD operations
- JWT authentication interceptor
- Error handling
- Type-safe interfaces for User model
What’s Next?
In Part 10, we’ll explore Routing & Navigation – how to build single-page application navigation!