Picture of Brian Love wearing black against a dark wall in Portland, OR.

Brian Love

Migrating to HttpClient

Migrating to the new Angular HttpClientModule from the deprecated HttpModule.

The Angular team has released the new HttpClientModule module and is deprecating the HttpModule. While you can currently still use the deprecated module, and both will live alongside each other for some time, there are some benefits to using the new module. Some of the new features, as per the documentation, include:

HttpClient Blob

Update: if you are working with the responseType of “blob”, check out my new post on using Angular HttpClient Blob.

Sample Application

I’m going to be using an existing sample application that I wrote and blogged about in the [MEAN App Unplugged series](% post_url 2017-07-16-mean-app-unplugged %})

To get started, clone the sample mean-material-reactive application:

$ git clone https://github.com/blove/mean-material-reactive.git
$ cd mean-material-reactive
$ git checkout -b initial-app

Then, run the gulp tasks and start the Node.js Express server. This will create a simple REST API endpoint at http://localhost:8080/heros.

$ gulp
$ chmod +x ./dist/bin/www
$ ./dist/bin/www

Then, serve the Angular client using the CLI:

$ ng serve

You should be able to visit http://localhost:4200 in your browser and see the Tour of Heros app that I wrote. If you look at the client/src/app/core/services/heros.service.ts file you will see that I wrote an injectable service for communicating with the REST API that is running in Node.js. You will also note that I am currently using the deprecated Http service. Our task is to migrate to the new HttpClient.

Install

First, we need to make sure we are using Angular 4.3.0, or greater. At the time of this writing, Angular 4.3.1 is the most current stable release.

Next, import the HttpClientModule module in the AppModule module in client/src/app/app.module.ts:

import { HttpClientModule } from "@angular/common/http";

@NgModule({
  imports: [
    BrowserModule
    // include HttpClientModule after BrowserModule
    HttpClientModule
  ]

  // code omitted
})
export class AppModule { }

Don’t forget to remove the import of the deprecated HttpModule module.

Previous Http Service

Here is what the HerosService looked like using the Http service:

import { Injectable } from '@angular/core';
import { Http, Response } from "@angular/http";

// rxjs
import { Observable } from "rxjs/Observable";

// models
import { Hero } from "../../models/hero";

@Injectable()
export class HerosService {

  private readonly URL = "http://localhost:8080/api/heros"

  constructor(
    protected http: Http,
  ) {}

  public create(hero: Hero): Observable<Hero> {
    return this.http
      .post(this.URL, hero)
      .map(this.extractObject);
  }

  public delete(hero: Hero): Observable<Hero> {
    return this.http
      .delete(`${this.URL}/${hero._id}`)
      .map(result => hero);
  }

  public get(id: string): Observable<Hero> {
    return this.http
      .get(`${this.URL}/${id}`)
      .map(this.extractObject);
  }

  public list(): Observable<Array<Hero>> {
    return this.http
      .get(this.URL)
      .map(response => response.json() || []);
  }

  public update(hero: Hero): Observable<Hero> {
    return this.http
      .put(`${this.URL}/${hero._id}`, hero)
      .map(this.extractObject);
  }

  private extractObject(res: Response): Object {
    const data: any = res.json();
    return data || {};
  }

}

There are a few things to note related to the migration:

New HttpClient Service

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

// rxjs
import { Observable } from "rxjs/Observable";

// models
import { Hero } from "../../models/hero";

@Injectable()
export class HerosService {

  private readonly URL = "http://localhost:8080/api/heros"

  constructor(
    protected httpClient: HttpClient,
  ) {}

  public create(hero: Hero): Observable<Hero> {
    return this.httpClient.post<Hero>(this.URL, hero);
  }

  public delete(hero: Hero): Observable<Hero> {
    return this.httpClient.delete<Hero>(`${this.URL}/${hero._id}`);
  }

  public get(id: string): Observable<Hero> {
    return this.httpClient.get<Hero>(`${this.URL}/${id}`);
  }

  public list(): Observable<Array<Hero>> {
    return this.httpClient.get<Array<Hero>>(this.URL);
  }

  public update(hero: Hero): Observable<Hero> {
    return this.httpClient.put<Hero>(`${this.URL}/${hero._id}`, hero);
  }

}

There are some great improvements here:

Query Params

I am not using any query string parameters, but it is worth noting here that all HTTP verb methods have an additional options object argument. The options object includes a params property, which is of type HttpParam. The HttpParam class can be thought of as a wrapper to a Map object. So, adding additional parameters is similar to working with a map. For example:

public get(id: string): Observable<Hero> {
  return this.httpClient.get<Hero>(this.URL, {
    params: new HttpParams().set("id", id)
  });
}

In the example above I am providing the options object with the params property, which is a new HttpParams object. Further, I set the id parameter to the string value of the id argument. Don’t forget to import the HttpParams object from the @angular/common/http module.

Response Type

If you are returning data that is not a JSON object you can override the responseType string value to one of the following in the options argument:

To learn more about the response types, dealing with errors, and using interceptors, check out the official documentation on the HttpClient in Angular 4.3