Angular HTTP Client & APIs
What is the Angular HTTP Client?
Most web applications need to communicate with a backend server — to fetch data, save records, or authenticate users. Angular provides the HttpClient module to handle all HTTP communication. It supports all standard HTTP methods (GET, POST, PUT, PATCH, DELETE) and returns the results as Observables, which are part of the RxJS library.
The HttpClient is a service provided by Angular. It must be imported into the application module before use, and then it can be injected into any service or component through dependency injection.
Setting Up HttpClient
Import HttpClientModule in the application's root module:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule // ← Add this
],
bootstrap: [AppComponent]
})
export class AppModule { }
Making HTTP Requests in a Service
HTTP requests should be placed in services, not directly in components. This keeps the component clean and makes the HTTP logic reusable.
GET Request — Fetching Data
This example uses JSONPlaceholder, a free public API that returns sample data, to demonstrate HTTP requests:
// post.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
interface Post {
id: number;
title: string;
body: string;
userId: number;
}
@Injectable({ providedIn: 'root' })
export class PostService {
private apiUrl = 'https://jsonplaceholder.typicode.com/posts';
constructor(private http: HttpClient) {}
// GET all posts
getAllPosts(): Observable<Post[]> {
return this.http.get<Post[]>(this.apiUrl);
}
// GET a single post by ID
getPostById(id: number): Observable<Post> {
return this.http.get<Post>(`${this.apiUrl}/${id}`);
}
}
Using the Service in a Component
// posts.component.ts
import { Component, OnInit } from '@angular/core';
import { PostService } from '../services/post.service';
@Component({
selector: 'app-posts',
templateUrl: './posts.component.html'
})
export class PostsComponent implements OnInit {
posts: any[] = [];
isLoading = true;
errorMessage = '';
constructor(private postService: PostService) {}
ngOnInit() {
this.postService.getAllPosts().subscribe({
next: (data) => {
this.posts = data;
this.isLoading = false;
},
error: (err) => {
this.errorMessage = 'Failed to load posts. Please try again.';
this.isLoading = false;
console.error(err);
}
});
}
}
<!-- posts.component.html -->
<h3>Blog Posts</h3>
<p *ngIf="isLoading">Loading posts...</p>
<p *ngIf="errorMessage">{{ errorMessage }}</p>
<div *ngFor="let post of posts | slice:0:5">
<h4>{{ post.title | titlecase }}</h4>
<p>{{ post.body | slice:0:100 }}...</p>
</div>
POST Request — Sending Data
A POST request sends new data to the server to create a new record:
// In post.service.ts — add this method:
createPost(post: { title: string; body: string; userId: number }): Observable<any> {
return this.http.post(this.apiUrl, post);
}
// In the component:
newPost = { title: 'My New Post', body: 'This is the post content.', userId: 1 };
savePost() {
this.postService.createPost(this.newPost).subscribe({
next: (response) => {
console.log('Post created:', response);
alert('Post saved successfully!');
},
error: (err) => {
console.error('Error saving post:', err);
}
});
}
PUT and PATCH Requests — Updating Data
// PUT — replaces the entire record
updatePost(id: number, post: any): Observable<any> {
return this.http.put(`${this.apiUrl}/${id}`, post);
}
// PATCH — updates only the specified fields
patchPost(id: number, changes: any): Observable<any> {
return this.http.patch(`${this.apiUrl}/${id}`, changes);
}
DELETE Request — Removing Data
deletePost(id: number): Observable<any> {
return this.http.delete(`${this.apiUrl}/${id}`);
}
// Component usage:
onDelete(postId: number) {
this.postService.deletePost(postId).subscribe({
next: () => {
this.posts = this.posts.filter(p => p.id !== postId);
alert('Post deleted.');
},
error: (err) => console.error('Delete failed:', err)
});
}
Sending HTTP Headers
Many APIs require authentication tokens or specific content types sent in the request headers:
import { HttpClient, HttpHeaders } from '@angular/common/http';
sendAuthenticatedRequest(): Observable<any> {
const headers = new HttpHeaders({
'Authorization': 'Bearer my-auth-token-here',
'Content-Type': 'application/json'
});
return this.http.get('https://api.example.com/protected-data', { headers });
}
Sending Query Parameters
Query parameters can be attached to HTTP requests using HttpParams:
import { HttpClient, HttpParams } from '@angular/common/http';
searchPosts(keyword: string, page: number): Observable<any> {
const params = new HttpParams()
.set('q', keyword)
.set('page', page.toString())
.set('limit', '10');
return this.http.get('https://api.example.com/search', { params });
// Resulting URL: https://api.example.com/search?q=angular&page=1&limit=10
}
HTTP Interceptors
An HTTP interceptor sits between the application and the server. It can inspect, modify, or handle every HTTP request and response that passes through. Common uses include automatically adding authentication tokens to every request, logging, or handling errors globally.
// auth.interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpInterceptor, HttpRequest, HttpHandler, HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('auth_token');
if (token) {
// Clone the request and add the authorization header
const authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
return next.handle(authReq);
}
return next.handle(req);
}
}
// Register the interceptor in app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './interceptors/auth.interceptor';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true // Allow multiple interceptors
}
]
})
export class AppModule { }
Error Handling with RxJS Operators
The catchError operator from RxJS handles errors that occur during HTTP requests inside the service itself, before they reach the component:
import { catchError, throwError } from 'rxjs';
getAllPosts(): Observable<Post[]> {
return this.http.get<Post[]>(this.apiUrl).pipe(
catchError(error => {
if (error.status === 404) {
console.error('Resource not found');
} else if (error.status === 401) {
console.error('Unauthorized — please log in');
} else {
console.error('An unexpected error occurred');
}
return throwError(() => new Error(error.message));
})
);
}
Displaying Loading State with the Async Pipe
Instead of subscribing manually in the component, the async pipe can subscribe directly in the template, automatically managing the subscription lifecycle:
// posts-async.component.ts
import { Component } from '@angular/core';
import { PostService } from '../services/post.service';
export class PostsAsyncComponent {
posts$ = this.postService.getAllPosts(); // Observable, not subscribed yet
constructor(private postService: PostService) {}
}
<!-- Template subscribes via async pipe -->
<div *ngIf="posts$ | async as posts; else loading">
<div *ngFor="let post of posts">
<h4>{{ post.title }}</h4>
</div>
</div>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
Summary
Angular's HttpClient provides a clean API for making HTTP requests. It must be imported via HttpClientModule. All HTTP methods — GET, POST, PUT, PATCH, DELETE — are available through the HttpClient service. HTTP requests return Observables, which must be subscribed to (either manually or via the async pipe) to execute. HTTP logic belongs in services, not components. Headers and query parameters are added using HttpHeaders and HttpParams. HTTP interceptors automatically handle cross-cutting concerns like authentication tokens for every request. The catchError RxJS operator handles errors inside services, keeping error handling centralized.
