🅰️ Angular Q22 / 128

How does Angular handle HTTP requests?

AI-Powered Answer ✓ Answered

Angular provides a powerful and convenient way to interact with backend services through its `HttpClient` service, part of `@angular/common/http`. This module simplifies making various types of HTTP requests (GET, POST, PUT, DELETE) and offers features like typed responses, request/response interception, and robust error handling.

1. The `HttpClient` Service

The HttpClient is Angular's modern HTTP client, built on top of the browser's XMLHttpRequest interface but offering a more streamlined and Angular-friendly API. It's designed to be used with RxJS Observables, providing powerful reactive programming capabilities for managing asynchronous data streams.

Key features include type-safe request and response objects, built-in support for JSON parsing, request and response interceptors, progress events, and a robust error handling mechanism.

2. Setup: Importing `HttpClientModule`

Before using HttpClient, you need to import HttpClientModule into your root AppModule or any feature module where you plan to use it.

typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; // Import HttpClientModule

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule // Add to imports array
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3. Injecting `HttpClient`

You can inject the HttpClient service into your components, services, or other injectable classes using Angular's dependency injection system.

typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) { }

  getUsers() {
    return this.http.get<User[]>('/api/users');
  }
}

4. Making HTTP Requests

HttpClient provides methods for all common HTTP verbs: get(), post(), put(), delete(), patch(), and jsonp(). All these methods return an RxJS Observable.

GET Request

To fetch data, use the get() method. You can specify the expected response type using generics for type safety.

typescript
interface Product {
  id: number;
  name: string;
  price: number;
}

// In a service or component:
getProducts() {
  return this.http.get<Product[]>('/api/products');
}

POST Request

To send data to the server (e.g., create a new resource), use the post() method, passing the data as the second argument.

typescript
interface NewProduct {
  name: string;
  price: number;
}

// In a service or component:
addProduct(product: NewProduct) {
  return this.http.post<Product>('/api/products', product);
}

Other Methods (PUT, DELETE)

put() is used for updating resources, and delete() for removing them. They follow a similar pattern.

typescript
// Update a product
updateProduct(id: number, product: Product) {
  return this.http.put<Product>(`/api/products/${id}`, product);
}

// Delete a product
deleteProduct(id: number) {
  return this.http.delete<void>(`/api/products/${id}`);
}

5. Subscribing to Observables

HTTP requests are *lazy* Observables. This means the request is not sent until you subscribe() to the Observable returned by HttpClient methods.

typescript
// In a component or another service
constructor(private userService: UserService) { }

ngOnInit() {
  this.userService.getUsers().subscribe({
    next: (users) => {
      console.log('Users fetched:', users);
      // Process users data
    },
    error: (err) => {
      console.error('Error fetching users:', err);
      // Handle error
    },
    complete: () => console.log('User fetching completed.')
  });
}

6. Advanced Features

HTTP Interceptors

Interceptors allow you to intercept outgoing HTTP requests and incoming HTTP responses. They can be used for various tasks like adding authentication headers, logging, error handling, or transforming data. They are chained, meaning multiple interceptors can process a request sequentially.

Error Handling

Errors from HTTP requests can be handled using the catchError operator from RxJS, typically within the service making the request. This allows you to gracefully manage network errors, server errors, and other exceptions.

typescript
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

// In UserService:
getUsers() {
  return this.http.get<User[]>('/api/users').pipe(
    catchError(error => {
      console.error('An error occurred:', error.message);
      return throwError(() => new Error('Something bad happened; please try again later.'));
    })
  );
}

Request Configuration

You can pass an options object to HttpClient methods to configure various aspects of the request, such as headers, params (query parameters), reportProgress, responseType, and observe.

typescript
import { HttpHeaders, HttpParams } from '@angular/common/http';

getFilteredProducts(category: string, sort: string) {
  const headers = new HttpHeaders({
    'Authorization': 'Bearer my-token'
  });

  const params = new HttpParams()
    .set('category', category)
    .set('sort', sort);

  return this.http.get<Product[]>('/api/products', { headers, params });
}

Conclusion

Angular's HttpClient provides a robust, type-safe, and reactive way to manage HTTP communications within your application. By leveraging Observables, interceptors, and comprehensive configuration options, developers can build highly responsive and maintainable applications that seamlessly interact with backend services.