Brian Love
Angular + TypeScript Developer in Denver, CO

Migrating to HttpClient

Reading time ~4 minutes

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:

  • Typed, synchronous response body access, including support for JSON body types
  • JSON is an assumed default and no longer needs to be explicitly parsed
  • Interceptors allow middleware logic to be inserted into the pipeline
  • Immutable request/response objects
  • Progress events for both request upload and response download
  • Post-request verification & flush based testing framework

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

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:

  • First, for each response I had to invoke map() to property extract the JSON data.
  • Next, I had a private extractObject() method that would accept the Response from the HTTP request, and would safely return the JSON object, or an empty object. Note the return type is just Object, since we don’t necessary know what type of object will be parsed and returned from the JSON.
  • Finally, we injected the Http service in the constructor method.

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:

  • First, we import the HttpClient and inject it via the constructor() function.
  • Next, the HttpClient allows for us to specify the response type using a generic. Note the type definition in brackets after invoking the HTTP verb, for example get().
  • We do not need to invoke map() to parse out the response data using the extractData() method. This method has been removed, and we simply return the Observable. The default responseType is JSON, and as such, the response data is already parsed for us.

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:

  • text
  • blob
  • arraybuffer

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

Brian Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Denver and I ski (a lot).