0% found this document useful (0 votes)
77 views7 pages

AngularIo - BuiltInFeatures - HttpClient 1

The document discusses communicating with backend services using Angular's HttpClient module. It provides an overview of HttpClient's major features like typed response objects and streamlined error handling. It then demonstrates how to make GET requests with HttpClient to retrieve configuration data from a backend server, including specifying the response type, handling the full response, and error handling. The examples show defining a Config service to encapsulate HTTP requests and consume the responses in a Config component.

Uploaded by

Jhon Hernández
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
77 views7 pages

AngularIo - BuiltInFeatures - HttpClient 1

The document discusses communicating with backend services using Angular's HttpClient module. It provides an overview of HttpClient's major features like typed response objects and streamlined error handling. It then demonstrates how to make GET requests with HttpClient to retrieve configuration data from a backend server, including specifying the response type, handling the full response, and error handling. The examples show defining a Config service to encapsulate HTTP requests and consume the responses in a Config component.

Uploaded by

Jhon Hernández
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 7

Communicating with backend services using HTTP

AngularIo_BuiltInFeatures_HttpClient 1

Most front-end applications need to communicate with a server over the HTTP protocol, in order to download or upload data and
access other back-end services. Angular provides a simplified client HTTP API for Angular applications, the HttpClient service class in
@angular/common/http.

The HTTP client service offers the following major features.

The ability to request typed response objects.


Streamlined error handling.
Testability features.
Request and response interception.
Prerequisites
Before working with the HTTPClientModule, you should have a basic understanding of the following:

TypeScript programming
Usage of the HTTP protocol
Angular app-design fundamentals, as described in Angular Concepts
Observable techniques and operators. See the Observables guide.
Setup for server communication
Before you can use HttpClient, you need to import the Angular HttpClientModule. Most apps do so in the root AppModule.

app/app.module.ts (excerpt)
content_copy
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
],
declarations: [
AppComponent,
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
You can then inject the HttpClient service as a dependency of an application class, as shown in the following ConfigService example.

app/config/config.service.ts (excerpt)
content_copy
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class ConfigService {
constructor(private http: HttpClient) { }
}
The HttpClient service makes use of observables for all transactions. You must import the RxJS observable and operator symbols
that appear in the example snippets. These ConfigService imports are typical.

app/config/config.service.ts (RxJS imports)


content_copy
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
You can run the live example / download example that accompanies this guide.

The sample app does not require a data server. It relies on the Angular in-memory-web-api, which replaces the HttpClient module's
HttpBackend. The replacement service simulates the behavior of a REST-like backend.

Look at the AppModule imports to see how it is configured.

Requesting data from a server


Use the HTTPClient.get() method to fetch data from a server. The asynchronous method sends an HTTP request, and returns an
Observable that emits the requested data when the response is received. The return type varies based on the observe and
responseType values that you pass to the call.

The get() method takes two arguments; the endpoint URL from which to fetch, and an options object that you can use to configure
the request.

content_copy
options: {
headers?: HttpHeaders | {[header: string]: string | string[]},
observe?: 'body' | 'events' | 'response',
params?: HttpParams|{[param: string]: string | string[]},
reportProgress?: boolean,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
}
Important options include the observe and responseType properties.

The observe option specifies how much of the response to return.


The responseType option specifies the format in which to return data.
You can use the options object to configure various other aspects of an outgoing request. In Adding headers, for example, the
service set the default headers using the headers option property.

Use the params property to configure a request with HTTP URL parameters, and the reportProgress option to listen for progress
events when transferring large amounts of data.

Applications often request JSON data from a server. In the ConfigService example, the app needs a configuration file on the server,
config.json, that specifies resource URLs.
assets/config.json
content_copy
{
"heroesUrl": "api/heroes",
"textfile": "assets/textfile.txt"
}
To fetch this kind of data, the get() call needs the following options: {observe: 'body', responseType: 'json'}. These are the default
values for those options, so the following examples do not pass the options object. Later sections show some of the additional
option possibilities.

The example conforms to the best practices for creating scalable solutions by defining a re-usable injectable service to perform the
data-handling functionality. In addition to fetching data, the service can post-process the data, add error handling, and add retry
logic.

The ConfigService fetches this file using the HttpClient.get() method.

app/config/config.service.ts (getConfig v.1)


content_copy
configUrl = 'assets/config.json';

getConfig() {
return this.http.get(this.configUrl);
}
The ConfigComponent injects the ConfigService and calls the getConfig service method.

Because the service method returns an Observable of configuration data, the component subscribes to the method's return value.
The subscription callback performs minimal post-processing. It copies the data fields into the component's config object, which is
data-bound in the component template for display.

app/config/config.component.ts (showConfig v.1)


content_copy
showConfig() {
this.configService.getConfig()
.subscribe((data: Config) => this.config = {
heroesUrl: data.heroesUrl,
textfile: data.textfile
});
}
Requesting a typed response
You can structure your HttpClient request to declare the type of the response object, to make consuming the output easier and
more obvious. Specifying the response type acts as a type assertion at compile time.

Specifying the response type is a declaration to TypeScript that it should treat your response as being of the given type. This is a
build-time check and doesn't guarantee that the server will actually respond with an object of this type. It is up to the server to
ensure that the type specified by the server API is returned.
To specify the response object type, first define an interface with the required properties. Use an interface rather than a class,
because the response is a plain object that cannot be automatically converted to an instance of a class.

content_copy
export interface Config {
heroesUrl: string;
textfile: string;
}
Next, specify that interface as the HttpClient.get() call's type parameter in the service.

app/config/config.service.ts (getConfig v.2)


content_copy
getConfig() {
// now returns an Observable of Config
return this.http.get<Config>(this.configUrl);
}
When you pass an interface as a type parameter to the HttpClient.get() method, you can use the RxJS map operator to transform
the response data as needed by the UI. You can then pass the transformed data to the async pipe.

The callback in the updated component method receives a typed data object, which is easier and safer to consume:

app/config/config.component.ts (showConfig v.2)


content_copy
config: Config;

showConfig() {
this.configService.getConfig()
// clone the data object, using its known Config shape
.subscribe((data: Config) => this.config = { ...data });
}
To access properties that are defined in an interface, you must explicitly convert the plain object you get from the JSON to the
required response type. For example, the following subscribe callback receives data as an Object, and then type-casts it in order to
access the properties.

content_copy
.subscribe(data => this.config = {
heroesUrl: (data as any).heroesUrl,
textfile: (data as any).textfile,
});
*OBSERVE* AND *RESPONSE* TYPES
The types of the observe and response options are string unions, rather than plain strings.

content_copy
options: {
...
observe?: 'body' | 'events' | 'response',
...
responseType?: 'arraybuffer'|'blob'|'json'|'text',
...
}
This can cause confusion. For example:

content_copy
// this works
client.get('/foo', {responseType: 'text'})

// but this does NOT work


const options = {
responseType: 'text',
};
client.get('/foo', options)
In the second case, TypeScript infers the type of options to be {responseType: string}. The type is too wide to pass to HttpClient.get
which is expecting the type of responseType to be one of the specific strings. HttpClient is typed explicitly this way so that the
compiler can report the correct return type based on the options you provided.

Use as const to let TypeScript know that you really do mean to use a constant string type:

content_copy
const options = {
responseType: 'text' as const,
};
client.get('/foo', options);
Reading the full response
In the previous example, the call to HttpClient.get() did not specify any options. By default, it returned the JSON data contained in
the response body.

You might need more information about the transaction than is contained in the response body. Sometimes servers return special
headers or status codes to indicate certain conditions that are important to the application workflow.

Tell HttpClient that you want the full response with the observe option of the get() method:

content_copy
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(
this.configUrl, { observe: 'response' });
}
Now HttpClient.get() returns an Observable of type HttpResponse rather than just the JSON data contained in the body.

The component's showConfigResponse() method displays the response headers as well as the configuration:

app/config/config.component.ts (showConfigResponse)
content_copy
showConfigResponse() {
this.configService.getConfigResponse()
// resp is of type `HttpResponse<Config>`
.subscribe(resp => {
// display its headers
const keys = resp.headers.keys();
this.headers = keys.map(key =>
`${key}: ${resp.headers.get(key)}`);

// access the body directly, which is typed as `Config`.


this.config = { ... resp.body };
});
}
As you can see, the response object has a body property of the correct type.

Making a JSONP request


Apps can use the HttpClient to make JSONP requests across domains when a server doesn't support CORS protocol.

Angular JSONP requests return an Observable. Follow the pattern for subscribing to observables and use the RxJS map operator to
transform the response before using the async pipe to manage the results.

In Angular, use JSONP by including HttpClientJsonpModule in the NgModule imports. In the following example, the searchHeroes()
method uses a JSONP request to query for heroes whose names contain the search term.

content_copy
/* GET heroes whose name contains search term */
searchHeroes(term: string): Observable {
term = term.trim();

const heroesURL = `${this.heroesURL}?${term}`;


return this.http.jsonp(heroesUrl, 'callback').pipe(
catchError(this.handleError('searchHeroes', [])) // then handle the error
);
}
This request passes the heroesURL as the first parameter and the callback function name as the second parameter. The response is
wrapped in the callback function, which takes the observables returned by the JSONP method and pipes them through to the error
handler.

Requesting non-JSON data


Not all APIs return JSON data. In this next example, a DownloaderService method reads a text file from the server and logs the file
contents, before returning those contents to the caller as an Observable<string>.

app/downloader/downloader.service.ts (getTextFile)
content_copy
getTextFile(filename: string) {
// The Observable returned by get() is of type Observable<string>
// because a text response was specified.
// There's no need to pass a <string> type parameter to get().
return this.http.get(filename, {responseType: 'text'})
.pipe(
tap( // Log the result or error
data => this.log(filename, data),
error => this.logError(filename, error)
)
);
}
HttpClient.get() returns a string rather than the default JSON because of the responseType option.

The RxJS tap operator (as in "wiretap") lets the code inspect both success and error values passing through the observable without
disturbing them.

A download() method in the DownloaderComponent initiates the request by subscribing to the service method.

app/downloader/downloader.component.ts (download)
content_copy
download() {
this.downloaderService.getTextFile('assets/textfile.txt')
.subscribe(results => this.contents = results);
}

You might also like