Learn Angular - HTTP & Data Fetching
Episode 8 of 24

Learn Angular - HTTP & Data Fetching

This episode covers HTTP and data fetching in Angular: using HttpClient for REST API integration, working with Observables and RxJS, handling errors with retry, and caching and request optimization strategies.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Modern applications almost never stand alone — they need data from a server. Angular provides HttpClient for HTTP communication, built on top of RxJS Observables, which are the main async pattern throughout the framework.

Episode 8 covers REST API integration with HttpClient, working with Observables and RxJS operators, error handling with retry, and caching and request optimization. These are core skills for any application that talks to a backend.

HttpClient and REST API Integration

Enabling HttpClient

HttpClient is enabled via provideHttpClient in app.config.ts:

JSEnable HttpClient
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()),
  ],
};

provideHttpClient(withFetch()) enables HttpClient and uses the modern fetch API as its engine. After this, services can inject HttpClient.

Your First Request with HttpClient

JSPosts service with HttpClient
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Post } from './post.model';
 
@Injectable({ providedIn: 'root' })
export class PostService {
  private readonly http = inject(HttpClient);
  private readonly apiUrl = 'https://jsonplaceholder.typicode.com';
 
  listar(): Observable<Post[]> {
    return this.http.get<Post[]>(`${this.apiUrl}/posts`);
  }
 
  cari(id: number): Observable<Post> {
    return this.http.get<Post>(`${this.apiUrl}/posts/${id}`);
  }
 
  buat(data: Post): Observable<Post> {
    return this.http.post<Post>(`${this.apiUrl}/posts`, data);
  }
}

this.http.get<Post[]>(url) returns an Observable<Post[]>. TypeScript generics here aren't a runtime guarantee, but they help you write correct code. post, put, delete, and patch follow the same pattern.

Observables, RxJS, and Data Streams

Subscribing in a Component

An Observable doesn't run until it's subscribed to. The common pattern in a component: call a service method, subscribe, then store the result.

JSSubscribe to an Observable in a component
import { Component, inject, signal, OnInit } from '@angular/core';
import { PostService } from './post.service';
 
@Component({
  selector: 'app-post-list',
  standalone: true,
  template: `
    @for (post of posts(); track post.id) {
      <p>{{ post.title }}</p>
    }
  `,
})
export class PostListComponent implements OnInit {
  private readonly postService = inject(PostService);
  readonly posts = signal<Post[]>([]);
 
  ngOnInit(): void {
    this.postService.listar().subscribe({
      next: (data) => this.posts.set(data),
      error: (err) => console.error('Gagal memuat post', err),
    });
  }
}

The HTTP result is stored in a signal so the template reacts to changes. Notice the error handler — without it, an HTTP error becomes an uncaught error in the console.

RxJS Operators for Data Pipelines

RxJS is a transformation pipeline. Some operators you'll use most often with HttpClient:

JSData pipeline with RxJS operators
import { map, tap, catchError, retry, throwError, of } from 'rxjs';
 
this.http.get<Post[]>(url).pipe(
  retry({ count: 3, delay: 1000 }),
  map((posts) => posts.filter((p) => p.published)),
  tap((posts) => console.log(`Dimuat ${posts.length} post`)),
  catchError((err) => {
    console.error(err);
    return of([]);
  }),
);

retry({ count: 3, delay: 1000 }) retries the request three times with a one-second delay for transient errors like timeouts. map transforms the data, tap executes side effects, and catchError catches failures — here it returns an empty list so the UI doesn't crash.

Error Handling and Retry

Distinguishing Error Types

An HTTP error isn't always the server's fault. HttpErrorResponse distinguishes client-side (network) and server-side (status code) errors:

JSCatching HTTP errors separately
import { HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
 
this.http.get<Post>(url).pipe(
  catchError((err: HttpErrorResponse) => {
    if (err.status === 404) {
      console.error('Data tidak ditemukan');
      return throwError(() => new Error('Post tidak ada'));
    }
    if (err.status >= 500) {
      console.error('Kesalahan server, coba lagi nanti');
      return throwError(() => err);
    }
    console.error('Masalah jaringan atau kesalahan lain', err.message);
    return throwError(() => err);
  }),
);

err.status === 404 marks data not found, while status 500 marks a server problem that may be transient. throwError forwards the error to the subscriber so the UI can show an appropriate message.

Caching and Request Optimization

Caching with RxJS

Without caching, every component that needs the same data makes a repeated request. Use the shareReplay operator to store the result and share it:

JSSimple caching with shareReplay
import { shareReplay } from 'rxjs';
 
private readonly cache$ = this.http.get<Post[]>(url).pipe(shareReplay(1));
 
listar(): Observable<Post[]> {
  return this.cache$;
}

shareReplay(1) runs the request once, then stores the result and shares it with all subsequent subscribers. This is the simplest form of caching at the data-stream level.

Request Optimization

A few optimization practices: use distinctUntilChanged to avoid duplicate requests, combine parallel requests with forkJoin or combineLatest, cancel unneeded requests with switchMap, and clean up subscriptions in ngOnDestroy. Deeper caching details, including cache invalidation and HTTP interceptors, will be covered in episode 14.

Wrap Up

Key takeaways:

  • provideHttpClient enables HttpClient in a standalone application.
  • HttpClient returns an Observable that only runs once subscribed.
  • retry, map, tap, and catchError form a safe data pipeline.
  • Distinguish network, 404, and server errors via HttpErrorResponse.
  • shareReplay provides simple caching for data that rarely changes.
  • Request optimization uses switchMap, forkJoin, and subscription cancellation.

In the next episode, episode 9, we'll cover routing and navigation — using the Angular Router, arranging route configuration with child routes and lazy loading, protecting routes with guards and resolvers, and managing query params, fragments, and navigation extras.

Learn Angular - HTTP & Data Fetching | Learn Angular