Angular - Http Client



HTTP is an application layer protocol on the Internet. It stands for Hypertext Transfer Protocol and is the foundation of any data exchange on the web. It transmits hypertext requests and information between a server and a browser (client).

What is HTTP Client?

HTTP Client is a client-side programming that provides the HTTP server access to various resources and services. It allows the client (a browser or application) to send HTTP requests and receive responses from the server.

HTTP client programming is an important feature in every modern web application. Nowadays, many applications expose their functionality through REST APIs, which work over the HTTP protocol.

To support this, the Angular team offers extensive tools to enable HTTP communication with servers. Angular provides a module called HttpClientModule and a service called HttpClient to handle HTTP programming.

The following diagram provides a clear understanding of the HTTP Client, and the request and response mechanism −

HTTP Client

Let us learn how to use the HttpClient service in this chapter. Developers should have a basic understanding of HTTP programming to understand the concepts discussed in this chapter.

Create a Server for Expense REST API

Configure Http client

Let's learn how to configure the HttpClientservice. You need to import the HttpClient in App Config:

provideHttpClient(withInterceptorsFromDi())

app.config.ts

import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
    provideHttpClient(withInterceptorsFromDi())
  ]
};

Create expense service

Let us create a new service named ExpenseEntryin your ExpenseManagerapplication to interact withExpense REST API. ExpenseEntryService will get the latest expense entries, insert new expense entries, modify existing expense entries, and delete the unwanted expense entries.

Open the command prompt and go to the project root folder:

cd /go/to/expense-manager

Start the application by running the below command:

ng serve

Run the below command to generate an Angular service with the name ExpenseEntry:

ng g s expense-entry

This will create two Typescript files (expense entry service & its testing file) as specified below:

CREATE src/app/expense-entry.spec.ts (364 bytes) 
CREATE src/app/expense-entry.ts (141 bytes)

Run the below command to generate an Angular Interface with the name Expense:

ng g i expense
CREATE src/app/expense.ts (141 bytes)

Now, open the Expense Interface (src/app/expense.ts) file.

export interface Expense {
    id: number,
    item: string,
    amount: number,
    category: string,
    location: string,
    spendOn: string,
    createdOn: string
}

Now, open the ExpenseEntry Service (src/app/expense-entry.service.ts) file. import ExpenseEntry, throwError, and catchErrorfrom the rxjs library, and importHttpClient, HttpHeaders, and HttpErrorResponse from @angular/common/http package:

import { Injectable } from '@angular/core'; 
import { Expense } from './expense'; 
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators'; 
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';

Inject the HttpClient service into our service:

constructor(private httpClient : HttpClient) { }

Create a variable, expenseRestUrl to specify the Expense Rest API endpoints:

private expenseRestUrl = 'http://localhost:8000/api/expense';

Create a variable,httpOptionsto set the HTTP Header option. This will be used during the Http Rest API call by the AngularHttpClientservice:

private httpOptions = { 
   headers: new HttpHeaders( { 'Content-Type': 'application/json' }) 
};

The complete code is as follows:

expense-entry.ts

import { Injectable } from '@angular/core';
import { Expense } from './expense';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';

@Injectable({
   providedIn: 'root'
})
export class ExpenseEntry {
   private expenseRestUrl = 'api/expense';
   private httpOptions = {
      headers: new HttpHeaders({'Content-Type': 'application/json'})
   };

   constructor(private httpClient : HttpClient) { }
}

HTTP GET

The HttpClient provides a get() method to fetch data from a web page. The main argument is the target web URL. Another optional argument is the option object with the below format −

{
   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,
}

Here,

  • headers: HTTP Headers of the request, either as string, an array of string, or an array of HttpHeaders.

  • observe: Process the response and return the specific content of the response. Possible values are body, response, and events. The default option of the observer is the body.
  • params: HTTP parameters of the request, either as string, array of string, or array of HttpParams.
  • reportProgress: Whether to report the progress of the process or not (true or false).
  • responseType: Refers to format of the response. Possible values are arraybuffer, blob, JSON, and text.
  • withCredentials: Whether the request has credentials or not (true or false).

Note: All options are optional, you can choose to use them or not.

The get() method returns the response of the request as Observable. The returned Observable emits the data when the response is received from the server.

The sample code to useget()method is as follows:

httpClient.get(url, options) .subscribe( (data) => console.log(data) );

Typed Response

Theget()method has an option to return observables, which emits a typed response as well. The sample code to get a typed response (ExpenseEntry) is as follows:

httpClient.get<T>(url, options) .subscribe( (data: T) => console.log(data) );

Handling errors

Error handling is one of the important aspects of HTTP programming. Encountering errors is one of the common scenarios in HTTP programming.

Errors in HTTP Programming can be categorized into two groups −

  • Client-side issues can occur due to network failure, misconfiguration, etc. If a client-side error happens, then theget()method throwsan ErrorEventobject.
  • Server-side issues can occur due to wrong URL, server unavailability, server programming errors, etc.

Let us write a simple error handling for our ExpenseEntry service.

private httpErrorHandler (error: HttpErrorResponse) {
   if (error.error instanceof ErrorEvent) {
      console.error("A client side error occurs. The error message is " 
	  + error.message);
      } else {
         console.error(
         "An error happened in server. The HTTP status code is "  
	     + error.status + " and the error returned is " + error.message);
      }
   return throwError("Error occurred. Pleas try again");
}

The error function can be called in get() as specified below −

httpClient.get(url, options)  
   .pipe(catchError(this.httpErrorHandler) 
   .subscribe( (data) => console.log(data) )

Handle failed request

As we mentioned earlier, errors can happen, and one way is to handle them by implementing the error handling. Another option is to try for a certain number of times. If the request fails due to a network issue or the HTTP server is temporarily offline, the next request may succeed.

We can usethe rxjslibrary"retry"operator in this scenario as specified below :

httpClient.get(url, options) 
   .pipe( 
      retry(5), 
      catchError(this.httpErrorHandler)) 
   .subscribe( (data) => console.log(data) )

Fetch expense entries

Let us do the actual coding to fetch the expenses from Expense Rest API in our ExpenseManager application.

Open the command prompt and go to the project root folder:

cd /go/to/expense-manager

Start the application by running the following command:

ng serve

Now, add the getExpenseEntries() and httpErrorHandler() methods in ExpenseEntryService (src/app/expense-entry.service.ts) service as follows:

getExpenseEntries() : Observable<Expense[]> {
   return this.httpClient.get<Expense[]>(this.expenseRestUrl, this.httpOptions)
   .pipe(retry(3),catchError(this.httpErrorHandler));
}

getExpenseEntry(id: number) : Observable<Expense> {
   return this.httpClient.get<Expense>(this.expenseRestUrl + "/" + id, this.httpOptions)
   .pipe(
      retry(3),
      catchError(this.httpErrorHandler)
   );
}

private httpErrorHandler (error: HttpErrorResponse) {
   if (error.error instanceof ErrorEvent) {
      console.error("A client side error occurs. The error message is " + error.message);
   } else {
      console.error(
         "An error happened in server. The HTTP status code is "  + 
		 error.status + " and the error returned is " + error.message);
   }

   return throwError("Error occurred. Pleas try again");
}

Here,

  • getExpenseEntries() calls the get() method using expense end point and also configures the error handler. Also, it configures httpClient to try for maximum of 3 times in case of failure. Finally, it returns the response from server as typed (ExpenseEntry[]) Observable object.
  • getExpenseEntry is similar to getExpenseEntries() except it passes the id of the ExpenseEntry object and gets ExpenseEntry Observable object.

expense-entry.ts

The complete coding of ExpenseEntry Service is as follows:

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Expense } from './expense';

@Injectable({

   providedIn: 'root'
})
export class ExpenseEntry {
   private expenseRestUrl = 'http://localhost:8000/api/expense';
   private httpOptions = {
      headers: new HttpHeaders( { 'Content-Type': 'application/json' })
   };

   constructor(private httpClient : HttpClient) { } 

   getExpenseEntries() : Observable<Expense[]> {
      return this.httpClient.get<Expense[]>(this.expenseRestUrl, this.httpOptions)
      .pipe(
         retry(3),
         catchError(this.httpErrorHandler)
      );
   }

   getExpenseEntry(id: number) : Observable<Expense> {
      return this.httpClient.get<Expense> (this.expenseRestUrl + "/" + id, this.httpOptions)
      .pipe(
         retry(3),
         catchError(this.httpErrorHandler)
      );
   }

   private httpErrorHandler (error: HttpErrorResponse) {
      if (error.error instanceof ErrorEvent) {
         console.error("A client side error occurs. The error message is " + error.message);
      } else {
         console.error(
            "An error happened in server. The HTTP status code is " 
			+ error.status + " and the error returned is " + error.message);
      }

      return throwError("Error occurred. Pleas try again");
   }
}

Open theExpenseEntryList and go to the "src-entry-list-entry-list.ts" file and inject ExpenseEntry Service through the constructor as specified below:

constructor(private debugService: Debug, private restService : ExpenseEntry ) { }

Change thegetExpenseEntries()method, and call the "getExpenseEntries()" method from ExpenseEntry Service instead of returning the mock items:

getExpenseItems() {  
   this.restService.getExpenseEntries() 
      .subscribe( data =− this.expenseEntries = data ); 
}

The completeExpenseEntryListcodes are as follows −

expense-entry-list.ts

import { Component, OnInit } from '@angular/core';
import { Debug } from '../debug';
import { Expense } from '../expense';
import { ExpenseEntry } from '../expense-entry';

@Component({
   selector: 'app-expense-entry-list',
   templateUrl: './expense-entry-list.html',
   styleUrls: ['./expense-entry-list.css'],
   providers: [Debug, ExpenseEntry]
})
export class ExpenseEntryList implements OnInit {
   title: string = '';
   expenseEntries: Expense[] = [];
   constructor(
   private debugService: Debug, 
   private restService : ExpenseEntry 
   ) { }

   ngOnInit() {
      this.debugService.info("Expense Entry List component initialized");
      this.title = "Expense Entry List";

      this.getExpenseItems();
   }

   getExpenseItems() {
      this.restService.getExpenseEntries()
      .subscribe( data => this.expenseEntries = data );
   }
}

Finally, check the application and you will see the below response:

failed request

HTTP POST

The HTTP POST is similar to HTTP GET except that the post request will send the necessary data as posted content along with the request. HTTP POST is used to insert new records into the system. HttpClientprovidesthe post()method, which is similar toget(), except it supports an extra argument to send the data to the server.

Let us add a new method, addExpenseEntry() in our ExpenseEntry Service to add new expense entry as mentioned below:

addExpenseEntry(expenseEntry: Expense): Observable<Expense> {
   return this.httpClient.post<ExpenseEntry>(this.expenseRestUrl, expenseEntry, this.httpOptions)
   .pipe(
      retry(3),
      catchError(this.httpErrorHandler)
   );
}

HTTP PUT

The HTTP PUT is similar to HTTP POST requests. This is used to update existing records in the system. The HttpClient provides a put()method, which is similar topost().

Update expense entry

Let us add a new method, updateExpenseEntry() in our ExpenseEntry Service to update existing expense entry as mentioned below:

updateExpenseEntry(expenseEntry: Expense): Observable<Expense> {
   return this.httpClient.put<Expense>(this.expenseRestUrl + "/" + expenseEntry.id, expenseEntry, this.httpOptions)
   .pipe(
      retry(3),
      catchError(this.httpErrorHandler)
   );
}

HTTP DELETE

The HTTP DELETE is similar to the HTTP GET request. It is used to delete entries in the system. The HttpClient provides a delete()method, which is similar toget().

Delete expense entry

Let us add a new method, deleteExpenseEntry() in our ExpenseEntry Service to delete existing expense entries as mentioned below:

deleteExpenseEntry(expenseEntry: Expense | number) : Observable<Expense> {
   const id = typeof expenseEntry == 'number' ? expenseEntry : expenseEntry.id
   const url = `${this.expenseRestUrl}/${id}`;

   return this.httpClient.delete<Expense>(url, this.httpOptions)
   .pipe(
      retry(3),
      catchError(this.httpErrorHandler)
   );
}

Multiple Choice Questions (MCQ's):

Here we have mentioned a few MCQs to test your knowledge on the current concept:

Answer : C

Explanation:

In Angular, the HttpClient performs HTTP requests to communicate with backend services.

Q 2 − What does HttpClient return when making requests?

A − String

B − Promise

C − JSON

D − Observable

Answer : D

Explanation:

In Angular, the HttpClient returns an Observable, allowing for asynchronous data handling.

Q 3 − Which method is provided by HttpClient to make HTTP requests?

A − send()

B − get()

C − fetch()

D − request()

Answer : B

Explanation:

The get() method is one of the methods provided by HttpClient to make HTTP requests in Angular.

Advertisements