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: