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](% 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:
- 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 theResponse
from the HTTP request, and would safely return the JSON object, or an empty object. Note the return type is justObject
, 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 theconstructor
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 theconstructor()
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 exampleget()
. - We do not need to invoke
map()
to parse out the response data using theextractData()
method. This method has been removed, and we simply return theObservable
. The defaultresponseType
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