Angular 18 REST API By Example with HttpClient

Angular 18 REST API By Example with HttpClient

In this article, we'll explore how to harness the power of Angular HttpClient to facilitate GET requests to REST API servers within your Angular 18 application. We'll delve into Angular services, RxJS Observables, models, and leverage the async pipe for streamlined data handling.

Front-end applications built on frameworks such as Angular establish communication with backend servers via REST APIs, which adhere to the HTTP protocol. Traditionally, this interaction was managed using the XMLHttpRequest interface or the fetch() API.

Angular simplifies this process with its HttpClient interface, which is built upon the XMLHttpRequest interface. Notably, this choice ensures compatibility with older browsers, making it a versatile and reliable solution for HTTP communication in Angular applications.

Throughout this angular tutorial, we'll look at practical examples of how to utilize the HttpClient class from the @angular/common/http package to perform HTTP GET requests with the get() method.

We'll delve into the following topics:

  • Establishing a mock, yet fully operational REST API to emulate backend functionality.
  • Step-by-step guide on Angular service creation and implementation.
  • Understanding the subscription paradigm for managing data streams with Observables.
  • Leveraging the async pipe within Angular templates to seamlessly iterate through Observable data, enhancing application efficiency and maintainability.

Prerequisites

Before you can begin, there are a few prerequisites you must meet. You must have the following tools installed on your development system in order to work properly:

  • Node.js and npm. You can install both from the official website.
  • Angular CLI 18 (You can install it from npm using: npm install -g @angular/cli)

You'll also need to generate an Angular 18 project to complete the task.

If this is your first time working with the Angular CLI, just open your terminal and enter the following command to create a project:

$ ng new AngularHttpClientGetDemo

Setting up Angular 18 HttpClient

To use HttpClient in your Angular 18 application, you need to import and provide HttpClient in your application's configuration file. Open the src/app/app.config.ts file and update it as follows:

import { ApplicationConfig } from '@angular/core';
[...]
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [..., provideHttpClient()]
};

All done, we are now prepared to use the HttpClient in our angular 18 project.

Setting up a Fake REST API Server

A REST API server is required in order to demonstrate how to utilize the HttpClient library. You may use an external API service, create a realistic Rest API server, or create a fake API using the json-server library to accomplish the task.

For the purpose of our demonstration, we'll utilize the json-server library to create a simulated REST API server. This approach offers a swift and convenient solution compared to integrating an external API service or developing a realistic REST API server from scratch.

So, open up your terminal and begin by installing json-server using the Node Package Manager (npm) as follows:

$ npm install -g json-server 

Next define your data in a db.json file:

            {
            "products": [
                {
                "id": 1,
                "name": "Product001",
                "cost": 10.0,
                "quantity": 1000,
                "locationId" : 1,
                "familyId" : 1
                },
                {
                "id": 2,
                "name": "Product002",
                "cost": 20.0,
                "quantity": 2000,
                "locationId" : 1,
                "familyId" : 2
                },   
                {
                "id": 3,
                "name": "Product003",
                "cost": 30.0,
                "quantity": 3000,
                "locationId" : 3,
                "familyId" : 2     
                },
                {
                "id": 4,
                "name": "Product004",
                "cost": 40.0,
                "quantity": 4000,
                "locationId" : 2,
                "familyId" : 3
                }
            ],
            "locations":[
                {
                "id": 1,
                "name": "Location001"
                },
                {
                "id": 2,
                "name": "Location002"
                },
                {
                "id": 3,
                "name": "Location003"
                }
            ],
            "families":[
                {
                "id": 1,
                "name": "FM001"
                },
                {
                "id": 2,
                "name": "FM002"
                },
                {
                "id": 3,
                "name": "FM003"
                }
            ],
            "transactions":[
                {
                "id": 1,
                "cost":11,
                "quantity":10,
                "productId":1
                },
                {
                "id": 2,
                "cost":12,
                "quantity":100,
                "productId":2
                },    
                {
                "id": 3,
                "cost":15,
                "quantity":101,
                "productId":3
                }  
            ]
            }

Following that, you may start a REST server by running the following command:

$ json-server --watch db.json 

The HttpClient get() Method

The HttpClient get() function is intended to be used to send HTTP GET requests to the server. As an example, consider the following syntax:

        get(url: string, options: {
            headers?: HttpHeaders;
            observe: 'response';
            params?: HttpParams;
            reportProgress?: boolean;
            responseType?: 'json';
            withCredentials?: boolean;
        }): Observable<HttpResponse<Object>>;

It takes a REST API endpoint and an optional options object and returns an Observable instance.

Now let's take a real world example. Let's presume you want to access the API endpoints we created above:

First you need to import HttpClient in your component.

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

Next you need to inject HttpClient via the component's constructor

  constructor(private httpClient: HttpClient){}

Next, add a method where you can call HttpClient.get(ENDPOINT_URL):

    get_products(){
        this.httpClient.get(this.baseUrl + '/products').subscribe((res)=>{
            console.log(res);
        });
    }

When called, this method will make a GET request to the /products endpoint then subscribe to the returned Observable. It will then log the array of products to the console.

Now let's make a button to callthe get_products() method:

    <button (click)="get_products()">GET /products</button>

Now, If you want to show the products on the component template.

First, add a products array:

    private products  = []; 

Next change the get_products() method as follows:

    get_products(){
        this.httpClient.get(this.baseUrl + '/products').subscribe((res : any[])=>{
        console.log(res);
        this.products = res;
        });
    }

We simply assing the returned products to the products array.

Next, use the ngFor directive in your component template to loop through the products array:

    <ul>
      <li *ngFor="let product of products" >
        -- id: {{product.id}}
        -- name: {{product.name}}
        -- cost: {{product.cost}}
        -- quantity: {{product.quantity}}
      </li>
    </ul> 

The async pipe and Observables

In our example, We can access the data returned by the get() method in two ways.

Subscribe to the returned Observable, i.e:

     get_products(){
        this.httpClient.get(this.baseUrl + '/products').subscribe((res : any[])=>{
            console.log(res);
            this.products = res;
        });
    }

Or use the async pipe with the returned Observable and iterate directly over data in the template. Let's see how in more details.

First, you need to create an Observable using the following:

     private productsObservable : Observable<any[]> ; 

Next, call the get() method and assign the result to productsObservable:

     this.productsObservable = this.httpClient.get(this.baseUrl + '/products');

Finally, in your template:

@for(product of productsObservable | async; track product.id){
      <li>
        -- id: {{product.id}}
        -- name: {{product.name}}
        -- cost: {{product.cost}}
        -- quantity: {{product.quantity}}
      </li>
}

We loop through the products using the Angular ngFor directive.

Using Angular 18 Services

Using code that access data directly in your components is against the separation of concerns rule so let's refactor our code to use an Angular service which makes HTTP GET requests then returns the result back to our component(s).

Using Angular CLI, generate a new service:

$ ng generate service data 

Next move the data access code to this service. Open the src/app/data.service.ts file and update it accordingly:

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

    @Injectable({
        providedIn: 'root'
    })
    export class DataService {
    baseUrl:string = "http://localhost:3000";

    constructor(private httpClient : HttpClient) {}

    get_products(){
        return this.httpClient.get(this.baseUrl + '/products');
    }
    get_families(){
        return this.httpClient.get(this.baseUrl + '/families');
    }
    get_locations(){
        return this.httpClient.get(this.baseUrl + '/locations');
    }
    get_transactions(){
        return this.httpClient.get(this.baseUrl + '/families');
    }

    }

Next, change the src/app/app.component.ts file as follows:

    import { Component } from '@angular/core';
    import { Observable } from 'rxjs';

    import { DataService } from './data.service';

    /* .... */
    @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
    })
    export class AppComponent {    
        private products  = []; 
        private families = [];
        private locations = [];
        private transactions = [];

        private productsObservable : Observable<any[]> ; 

        constructor(private dataService: DataService){

            this.productsObservable = this.dataService.get_products();

            this.dataService.get_families().subscribe((res : any[])=>{
                this.families = res;
            });
            this.dataService.get_locations().subscribe((res : any[])=>{
                console.log(res);
                this.locations = res;
            });
            this.dataService.get_transactions().subscribe((res : any[])=>{
                console.log(res);
                this.transactions = res;
            });    
        }
    }

Instead of injecting HttpClient directly in our component we inject our data service and call its methods to make GET requests to our REST API server.

Creating Angular Models

Now let's further refactor our code to use models for products, families, locations and transactions.

In the root of Angular project, create these models:

src/app/product.ts

    export interface Product {
        id: number;
        name: string;
        cost: number;
        quantity: number;
        locationId: number;
        familyId: number;
    } 

src/app/family.ts

    export interface Family {
        id: number;
        name: string;
    } 

src/app/location.ts

    export interface Location {
        id: number;
        name: string;
        constructor() { }
    } 

src/app/transaction.ts

    export interface Transaction {
        id: number;
        cost: number;
        productId: number;
        quantity: number;
    } 

Next update your the src/app/app.component.ts file to use the new models:

    import { Product } from './product';
    import { Family } from './family';
    import { Location } from './location';
    import { Transaction } from './transaction';


    private products : Product[] = []; 
    private families : Family[] = [];
    private locations : Location[] = [];
    private transactions : Transaction[] = [];

    private productsObservable : Observable<Product[]> ; 

    constructor(private dataService: DataService){

        this.productsObservable = this.dataService.get_products();

        this.dataService.get_families().subscribe((res : Family[])=>{
            this.families = res;
        });
        this.dataService.get_locations().subscribe((res : Location[])=>{
            this.locations = res;
        });
        this.dataService.get_transactions().subscribe((res : Transaction[])=>{
            this.transactions = res;
        });    
    }

You can find the complete source code of this demo in GitHub.

Conclusion

We explored the process of sending GET requests to REST API servers from within your Angular 18 application, delving into the utilization of HttpClient, Angular services, RxJS Observables, models, and leveraging the async pipe, among other key concepts.

Front-end applications, like those developed using Angular, rely on REST APIs to interact with backend servers. These interactions can be facilitated through either the XMLHttpRequest interface or the fetch() API.

Given that the XMLHttpRequest interface is supported even by older browsers, Angular's HttpClient was built upon this foundation to ensure broad compatibility.

Throughout our discussion, we covered the following significant aspects:

  • Crafting a simulated yet fully functional REST API.
  • Creating Angular Services, alongside subscribing to observables.
  • Employing the async pipe to iterate over Observable data seamlessly within templates.

  • Date: