DEV Community

Cover image for Angular HttpClient Architecture and Best Practices
josematoswork
josematoswork

Posted on • Originally published at angulardive.com

Angular HttpClient Architecture and Best Practices

Angular HttpClient Architecture and Best Practices

Angular is a popular JavaScript front-end framework used for building web applications. By default, Angular provides an HttpClient module that allows developers to make HTTP requests to the server, fetch data, and send data back to the server. In this article, we will be discussing the architecture of Angular HttpClient module and best practices to follow while using it.

What is HttpClient?

HttpClient is an Angular module that allows you to make HTTP requests to the server via a simple API. The HttpClient module provides methods for performing GET, POST, PUT, DELETE requests and more. The Angular team made it available as a replacement for the deprecated Http module that was available in Angular versions 2-4.

Architecture of HttpClient

The architecture of HttpClient can be broken down into two main parts:

    - The `@angular/common/http` namespace:
    import { HttpClient } from '@angular/common/http';
    - The `HttpClient` class:
    constructor(private http: HttpClient) { }

The @angular/common/http namespace includes all the classes required for making HTTP requests, such as HttpClient, HttpHeaders, HttpParams, etc. The HttpClient class provides methods for performing the actual HTTP request, as well as handling the response.

Importing HttpClient

To import HttpClient into an Angular component, you can use the following code:

import { HttpClient } from '@angular/common/http';
 
@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }
 
  getData() {
    return this.http.get('http://example.com/data');
  }
}

In the code above, we import the HttpClient class from the @angular/common/http namespace, and then inject it into our MyService class constructor. This allows us to use the HttpClient to make an HTTP GET request to http://example.com/data and fetch the data.

Using HttpHeaders with HttpClient

While making HTTP requests, you can also include headers in your requests. The HttpHeaders class allows you to create and set headers for HTTP requests:

import { HttpClient, HttpHeaders } from '@angular/common/http';
 
@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }
 
  postData(data: any) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/json'
    });
 
    return this.http.post('http://example.com/data', data, { headers });
  }
}

The code above includes the HttpHeaders class and creates a new header with a Content-Type of application/json. This header is then set in the post request options and sent with the data to http://example.com/data.

Best Practices for using HttpClient

Now that you know the basics of the HttpClient module, let's take a look at some best practices that should be followed when using it:

Always Use Observable object

The HttpClient module returns an Observable object by default. Observables can be subscribed to from the component, and can provide multiple values over time, unlike Promises that retrive a single value. This provides more flexibility for resolving values over an extended period of time. For exmaple:

export class MyComponent implements OnInit {
  items$: Observable<any[]>;
 
  constructor(private http: HttpClient) { }
 
  ngOnInit() {
    this.items$ = this.http.get<any[]>('http://example.com/data');
  }
}

In the code above, we use the Observable object returned by the HttpClient module and assign it to a property called items$, which can be subscribed to from the component. This provides more flexibility over resolving values over an extended period of time.

Handle Errors

When making HTTP requests, it is important to handle errors that may occur. This can be done using the catchError operator:

import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
 
@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }
 
  getData() {
    return this.http.get('http://example.com/data').pipe(
      catchError(err => {
        console.error(err);
        return throwError(err);
      })
    );
  }
}

In the code above, we use the catchError operator provided by the RxJS library to catch any error that may occur when making the HTTP request. We then log the error to the console and return it using the throwError operator.

Implement a caching strategy

When making repeated HTTP requests, it can be beneficial to implement a caching strategy. This can help reduce the number of requests made to the server and improve the performance of our application.

One way to implement caching is to use Angular's built-in cacheInterceptor. This can be done by creating a new HttpCacheInterceptor class:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
 
@Injectable()
export class HttpCacheInterceptor implements HttpInterceptor {
  private cache = new Map<string, HttpResponse<any>>();
 
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // try to retrieve a cached response
    const cachedResponse = this.cache.get(req.url);
 
    // return cached response
    if (cachedResponse) {
      return of(cachedResponse);
    }
 
    // send request to server and cache response
    return next.handle(req).pipe(
      tap(event => {
        if (event instanceof HttpResponse) {
          this.cache.set(req.url, event);
        }
      })
    );
  }
}

In the code above, we create a new HttpCacheInterceptor class that intercepts HTTP requests and responses. The cache variable is a Map object that stores cached responses. The intercept method first checks whether a cached response exists for the requested URL. If it exists, it returns the cached response, otherwise it sends the request to the server, caches the response, and returns it.

Testing HttpClient requests

When testing components that make HTTP requests using the HttpClient module, it is important to mock the HTTP requests. This can be done by providing a mock implementation of the HttpClient:

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let httpMock: HttpTestingController;
 
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [MyComponent]
    })
      .compileComponents();
  }));
 
  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    httpMock = TestBed.inject(HttpTestingController);
  });
 
  afterEach(() => {
    httpMock.verify();
  });
 
  it('should get data', () => {
    const mockData = [
      { id: 1, name: 'John' },
      { id: 2, name: 'Doe' }
    ];
 
    component.getData();
 
    const req = httpMock.expectOne('http://example.com/data');
    req.flush(mockData);
 
    expect(component.items).toEqual(mockData);
  });
});

In the code above, we create a new test for the MyComponent component. We first import the HttpClientTestingModule into the test module, and then create a mock implementation of the HttpClient using the HttpTestingController. We then create a new test that checks whether the getData method fetches data from the server and parses it correctly.

Conclusion

The Angular HttpClient module provides a simple and powerful API for making HTTP requests in your Angular application. With the information we have discussed in this article, you should now be able to use the HttpClient module with confidence and follow the best practices to make your application more scalable and maintainable.

Top comments (0)