Introduction
Modern web applications rarely work in isolation. Almost every real application communicates with a backend to fetch, create, update, or delete data.
In Angular, the recommended way to interact with APIs is by using services, and the built-in HttpClient module makes performing HTTP operations simple and consistent.
In this article, we will learn step by step how to create a reusable Angular service to handle API calls, including:
Configuring HttpClient
Creating an Angular Service
Making GET, POST, PUT, DELETE calls
Handling errors properly
Using Observables
Displaying data in a UI
Real-World Scenario
Imagine you are building a small product management module. You need operations like:
Get all products
Get product by ID
Add new product
Update product
Delete product
If you implement API calls directly inside multiple components, your project quickly becomes difficult to manage.
Instead, you create a single Angular service called ProductService that handles all API operations in one place. This makes your project clean, scalable, and easier to maintain.
Step 1: Enable HttpClient in Angular
Open app.module.ts and import HttpClientModule:
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [BrowserModule, HttpClientModule],
})
export class AppModule {}
Step 2: Generate a Service
Run the following command:
ng generate service services/product
Angular generates a file named:
product.service.ts
Step 3: Create the Service With API Methods
Example API URL:
https://localhost:5000/api/products
Service code
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private apiUrl = 'https://localhost:5000/api/products';
constructor(private http: HttpClient) {}
getProducts(): Observable<any> {
return this.http.get(this.apiUrl);
}
getProductById(id: number): Observable<any> {
return this.http.get(`${this.apiUrl}/${id}`);
}
addProduct(product: any): Observable<any> {
return this.http.post(this.apiUrl, product);
}
updateProduct(id: number, product: any): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, product);
}
deleteProduct(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
Step 4: Use the Service in a Component
Example: Show products in UI.
import { Component, OnInit } from '@angular/core';
import { ProductService } from '../services/product.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html'
})
export class ProductsComponent implements OnInit {
products: any[] = [];
constructor(private productService: ProductService) {}
ngOnInit(): void {
this.productService.getProducts().subscribe(data => {
this.products = data;
});
}
}
Component Template
<h2>Product List</h2>
<ul>
<li *ngFor="let product of products">
{{ product.name }} - Rs. {{ product.price }}
</li>
</ul>
Handling API Errors
Add RxJS catchError in service:
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
getProducts(): Observable<any> {
return this.http.get(this.apiUrl).pipe(
catchError(error => {
console.error('Error fetching products:', error);
return throwError(() => error);
})
);
}
Using a Loading Indicator
You can manage component UI state:
loading = true;
errorMessage = '';
ngOnInit() {
this.productService.getProducts().subscribe({
next: (data) => {
this.products = data;
this.loading = false;
},
error: (err) => {
this.errorMessage = 'Failed to load products';
this.loading = false;
}
});
}
Example Template With UI Feedback
<p *ngIf="loading">Loading...</p>
<p *ngIf="errorMessage">{{ errorMessage }}</p>
<ul *ngIf="!loading && !errorMessage">
<li *ngFor="let product of products">
{{ product.name }}
</li>
</ul>
Workflow Diagram
Component Requests Data
|
v
Angular Service Calls API
|
v
HttpClient Sends Request
|
v
API Response Received
|
v
Component Updates UI
Flowchart
START|
v
Call Service Method|
v
Send API Request via HttpClient
|+--- Error? ---> Display Error Message|
v
Update UI with Data
|
END
Best Practices
Keep all HTTP logic inside services.
Do not call APIs directly inside components.
Use models or interfaces instead of any.
Use environment variables for API URL.
Handle errors and display messages to users.
Common Mistakes and Solutions
| Mistake | Why it Happens | Solution |
|---|
| HttpClient not working | Module not imported | Add HttpClientModule |
| Values not showing | Forgetting subscribe | Always call .subscribe() |
| Duplicate code | Making API calls in multiple components | Use a shared service |
Conclusion
Angular services allow you to create reusable and scalable logic for API access. With HttpClient, making requests becomes clean and efficient. Once this concept is mastered, you can move into:
This is the foundation for a professional Angular application.