# Angular 20 di 2026: Resource API, httpResource, dan Pertanyaan Wawancara
> Angular 20 memperkenalkan httpResource dan menstabilkan Resource API untuk pengambilan data berbasis signal. Tutorial praktis yang mencakup resource(), rxResource(), httpResource(), validasi Zod, dan pertanyaan wawancara umum.
- Published: 2026-05-30
- Updated: 2026-06-06
- Author: SharpSkill
- Tags: angular, angular-20, resource-api, httpresource, signals, typescript, tutorial
- Reading time: 10 min
---
Angular 20 menempatkan [Resource API](https://angular.dev/guide/signals/resource) dan `httpResource` di garis depan pengambilan data berbasis signal. API eksperimental ini menggantikan pola subscription `HttpClient` yang bertele-tele dengan primitif reaktif yang secara otomatis melacak status loading, error, dan resolved.
> **Apa yang berubah di Angular 20**
>
> Resource API mengganti nama `request` menjadi `params` dan `loader` menjadi `stream` (untuk `rxResource`). Nilai status kini berupa literal string (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`) alih-alih enum numerik. `httpResource` dibangun di atas `HttpClient` dan mendukung interceptor serta validasi Zod secara bawaan.
## Tiga Varian Resource di Angular 20
Angular 20 menawarkan tiga cara untuk memuat data asinkron sebagai signal. Masing-masing menyasar kasus penggunaan yang berbeda, tetapi semuanya berbagi model reaktif yang sama: mendeklarasikan dependensi, mendefinisikan loader, lalu mengonsumsi hasilnya melalui signal.
- **`resource()`** bekerja dengan Promise. Ideal saat menggunakan `fetch()` atau API berbasis promise apa pun.
- **`rxResource()`** bekerja dengan Observable. Pilihan tepat ketika operator RxJS seperti `debounceTime`, `retry`, atau `switchMap` dibutuhkan.
- **`httpResource()`** membungkus `HttpClient` Angular secara langsung. Interceptor, utilitas pengujian, dan validasi schema berfungsi tanpa konfigurasi tambahan.
Perbedaan utama antara `httpResource` dan dua varian lainnya: `httpResource` menggunakan `HttpClient` di baliknya, yang berarti [interceptor](https://angular.dev/guide/http/interceptors) yang sudah ada tetap berfungsi. API `resource()` versi awal sepenuhnya melewati `HttpClient`, sebuah titik masalah besar di Angular 19.
## Membangun Profil Pengguna dengan resource()
Fungsi `resource()` menerima fungsi komputasi `params` dan fungsi `loader`. Ketika signal di dalam `params` berubah, loader dijalankan ulang secara otomatis.
```typescript
// user-profile.component.ts
import { Component, signal, resource } from '@angular/core';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
template: `
@if (userResource.hasValue()) {
{{ userResource.value().name }}
{{ userResource.value().email }}
} @else if (userResource.isLoading()) {
Loading profile...
} @else if (userResource.error()) {
Failed to load user
}
`,
})
export class UserProfileComponent {
userId = signal(1);
// params produces the reactive dependency
// loader receives it and returns a Promise
userResource = resource({
params: () => this.userId(),
loader: async ({ params: id, abortSignal }) => {
const res = await fetch(`/api/users/${id}`, { signal: abortSignal });
return res.json();
},
});
loadUser(id: number) {
this.userId.set(id); // triggers automatic refetch
}
}
```
Parameter `abortSignal` memungkinkan Angular membatalkan permintaan yang sedang berjalan ketika `userId` berubah sebelum permintaan sebelumnya selesai. Ini mencegah race condition tanpa pengelolaan subscription secara manual.
## Pengambilan Data Reaktif dengan httpResource
`httpResource` menghilangkan boilerplate dengan menggabungkan deklarasi URL dan eksekusi HTTP dalam satu panggilan. Ia mengembalikan `HttpResourceRef` yang mengekspos `value`, `isLoading`, `error`, `status`, dan `headers` sebagai signal.
```typescript
// product-list.component.ts
import { Component, signal, computed } from '@angular/core';
import { httpResource } from '@angular/common/http';
interface Product {
id: number;
name: string;
price: number;
category: string;
}
@Component({
selector: 'app-product-list',
template: `
@if (products.hasValue()) {
@for (product of products.value(); track product.id) {
{{ product.name }}
{{ product.price | currency }}
}
} @else if (products.isLoading()) {
Loading products...
}
`,
})
export class ProductListComponent {
category = signal('electronics');
// httpResource re-fetches whenever category() changes
products = httpResource(() => ({
url: '/api/products',
params: { category: this.category() },
}));
filterByCategory(cat: string) {
this.category.set(cat); // pending request is cancelled, new one starts
}
}
```
Beberapa detail penting di sini. Fungsi yang diteruskan ke `httpResource` mengembalikan objek konfigurasi permintaan. Angular melacak pembacaan signal di dalam fungsi tersebut, sehingga mengubah `category` memicu permintaan GET baru. Jika sebuah permintaan sedang berjalan, Angular membatalkannya sebelum memulai yang baru.
> **httpResource hanya untuk membaca**
>
> `httpResource` dirancang untuk pengambilan data (permintaan GET). Menggunakannya untuk operasi POST, PUT, atau DELETE tidak aman karena pembatalan permintaan dapat menggugurkan mutasi di tengah jalan. Untuk operasi tulis, gunakan `HttpClient` secara langsung atau bungkus mutasi dalam sebuah method service.
## Validasi Schema dengan Zod dan httpResource
Respons dari layanan eksternal bisa menyimpang dari bentuk yang diharapkan. Opsi `parse` pada `httpResource` mengintegrasikan pustaka validasi schema seperti [Zod](https://zod.dev/) untuk menangkap ketidaksesuaian saat runtime, alih-alih meneruskan data rusak secara diam-diam.
```typescript
// order.component.ts
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { z } from 'zod';
// Define the expected shape with Zod
const OrderSchema = z.object({
id: z.number(),
status: z.enum(['pending', 'shipped', 'delivered', 'cancelled']),
total: z.number().positive(),
items: z.array(z.object({
productId: z.number(),
quantity: z.number().int().positive(),
unitPrice: z.number().positive(),
})),
createdAt: z.string().datetime(),
});
type Order = z.infer;
@Component({
selector: 'app-order',
template: `
@if (order.hasValue()) {
Order #{{ order.value().id }}
Status: {{ order.value().status }}
Total: {{ order.value().total | currency }}
} @else if (order.error()) {
Invalid order data received
}
`,
})
export class OrderComponent {
orderId = signal(42);
// parse validates the response before exposing it as a signal
order = httpResource(
() => `/api/orders/${this.orderId()}`,
{ parse: OrderSchema.parse }
);
}
```
Ketika API mengembalikan data yang tidak cocok dengan `OrderSchema`, resource beralih ke status `'error'`. Tipe kembalian fungsi `parse` juga menentukan tipe TypeScript dari `value()`, sehingga definisi schema menjalankan peran ganda sebagai validator runtime sekaligus penghasil tipe.
## rxResource dengan stream dan params di Angular 20
Angular 20 mengganti nama `loader` menjadi `stream` dan `request` menjadi `params` pada `rxResource`. Perubahan ini menyelaraskan penamaan dengan semantik streaming yang didukung `rxResource`. Fungsi `stream` menerima konteks Observable dan harus mengembalikan sebuah Observable.
```typescript
// search.component.ts
import { Component, signal } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { debounceTime, switchMap } from 'rxjs';
interface SearchResult {
id: number;
title: string;
excerpt: string;
}
@Component({
selector: 'app-search',
template: `
@if (results.isLoading()) {
}
}
`,
})
export class SearchComponent {
private http = inject(HttpClient);
query = signal('');
// params (was "request") provides the reactive input
// stream (was "loader") returns an Observable
results = rxResource({
params: () => this.query(),
stream: ({ params: q }) =>
this.http.get('/api/search', {
params: { q },
}),
});
}
```
Berbeda dengan `httpResource`, `rxResource` memberikan kendali penuh atas pipeline Observable. Operator seperti `debounceTime` atau `retry` dapat dirangkai di dalam `stream`. Namun, `httpResource` menangani kasus paling umum (satu permintaan GET) dengan lebih sedikit kode.
## Pelacakan Status: Literal String Menggantikan Enum
Angular 20 mengubah `ResourceStatus` dari enum numerik menjadi tipe union string. Enam nilai yang mungkin memberikan wawasan rinci tentang siklus hidup resource:
| Status | Arti |
|---|---|
| `'idle'` | `params` mengembalikan `undefined` — tidak ada permintaan yang dikirim |
| `'loading'` | Permintaan pertama sedang berlangsung |
| `'reloading'` | Permintaan berikutnya setelah keberhasilan sebelumnya |
| `'resolved'` | Data tersedia di `value()` |
| `'error'` | Permintaan gagal — `error()` berisi error |
| `'local'` | Nilai diatur secara lokal melalui `.set()` atau `.update()` |
```typescript
// status-demo.component.ts
import { Component, signal, resource } from '@angular/core';
@Component({
selector: 'app-status-demo',
template: `
}
}
`,
})
export class StatusDemoComponent {
filter = signal(undefined);
data = resource({
params: () => this.filter(),
loader: async ({ params: f, abortSignal }) => {
const res = await fetch(`/api/data?filter=${f}`, { signal: abortSignal });
return res.json();
},
});
}
```
Mengembalikan `undefined` dari `params` menetapkan status menjadi `'idle'` dan mencegah loader dijalankan. Pola ini cocok untuk pengambilan data bersyarat — tampilkan petunjuk hingga pengguna memberikan masukan, lalu muat data.
> **Mengakses value() saat status error**
>
> Sejak Angular 20, memanggil `value()` pada resource berstatus `'error'` melempar exception saat runtime. Selalu lindungi pembacaan dengan `hasValue()` atau periksa `status()` sebelum mengakses `value()`. Ini adalah breaking change dari Angular 19, di mana `value()` mengembalikan `undefined` saat error.
## Pertanyaan Wawancara httpResource Angular 20
Resource API menjadi topik standar dalam [pertanyaan wawancara Angular](/blog/angular/angular-19-interview-questions-signals-ssr-must-know). Berikut pertanyaan yang menguji pemahaman nyata, melampaui kefasihan permukaan terhadap API.
**T: Masalah apa yang diselesaikan `httpResource` yang tidak diselesaikan `resource()`?**
`resource()` menggunakan `fetch()` atau loader berbasis promise mana pun, melewati `HttpClient` Angular. Ini berarti interceptor (untuk token autentikasi, logging, penanganan error) tidak berlaku. `httpResource` menggunakan `HttpClient` secara internal, sehingga interceptor, utilitas pengujian (`HttpTestingController`), dan fitur seperti `withFetch()` berfungsi tanpa konfigurasi tambahan.
**T: Kapan `rxResource` sebaiknya dipilih dibanding `httpResource`?**
`rxResource` memberikan kendali penuh atas Observable melalui fungsi `stream`-nya. Pilih ia ketika pipeline data membutuhkan operator RxJS — melakukan debounce pada masukan pencarian, mencoba ulang permintaan gagal dengan exponential backoff, atau menggabungkan beberapa stream dengan `combineLatest`. Untuk permintaan GET sederhana, `httpResource` membutuhkan lebih sedikit kode.
**T: Bagaimana Angular menangani permintaan bersamaan ketika sebuah signal berubah dengan cepat?**
Ketiga varian resource membatalkan permintaan yang sedang berjalan ketika `params` menghasilkan nilai baru. Untuk `httpResource` dan `resource()`, `AbortSignal` membatalkan fetch yang mendasarinya. Untuk `rxResource`, Angular berhenti berlangganan dari Observable sebelumnya. Ini mencegah respons usang menimpa data yang baru.
**T: Apa tujuan status `'local'`?**
Memanggil `.set()` atau `.update()` pada resource mengubah nilainya secara lokal tanpa memicu loader. Status beralih ke `'local'`, menandakan bahwa nilai saat ini tidak berasal dari server. Ini mendukung pembaruan UI optimistis — UI mencerminkan perubahan seketika sementara permintaan mutasi terpisah berjalan.
**T: Bagaimana integrasi Zod bekerja dengan `httpResource`?**
Opsi `parse` menerima fungsi apa pun dengan signature `(data: unknown) => T`. Ketika respons HTTP tiba, `httpResource` meneruskan JSON yang sudah diurai melalui `parse` sebelum menetapkan `value()`. Jika `parse` melempar exception (misalnya `ZodError`), resource beralih ke status `'error'`. Tipe kembalian `parse` menentukan tipe TypeScript dari `value()`, memberikan keamanan runtime sekaligus tipe saat kompilasi dari satu definisi schema.
Untuk pendalaman tentang [signal Angular](/technologies/angular/interview-questions/angular-signals) dan bagaimana mereka terintegrasi dengan framework secara lebih luas, modul signal mencakup computed signal, effect, dan model reaktivitas.
## Migrasi dari Subscription HttpClient ke httpResource
Aplikasi Angular yang sudah ada umumnya mengambil data dengan subscription `HttpClient` di dalam `ngOnInit`, atau menggunakan `AsyncPipe` dengan Observable. Migrasi ke `httpResource` mengikuti pola yang dapat diprediksi:
```typescript
// BEFORE: manual subscription in ngOnInit
@Component({ /* ... */ })
export class BeforeComponent implements OnInit, OnDestroy {
private http = inject(HttpClient);
private destroy$ = new Subject();
users: User[] = [];
loading = false;
error: string | null = null;
ngOnInit() {
this.loading = true;
this.http.get('/api/users')
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (data) => { this.users = data; this.loading = false; },
error: (err) => { this.error = err.message; this.loading = false; },
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
// AFTER: httpResource handles lifecycle automatically
@Component({ /* ... */ })
export class AfterComponent {
users = httpResource(() => '/api/users');
// No ngOnInit, no Subject, no manual unsubscribe
// Template uses users.value(), users.isLoading(), users.error()
}
```
Migrasi ini menghilangkan boilerplate pengelolaan siklus hidup. Pembersihan subscription terjadi otomatis ketika komponen dihancurkan. Status loading dan error sudah terpasang di dalam resource — tidak diperlukan flag boolean terpisah.
Untuk aplikasi yang sudah menggunakan [komponen standalone](/blog/angular/angular-standalone-components-migration-best-practices), migrasinya lugas: ganti injeksi `HttpClient` dan logika subscription dengan satu deklarasi `httpResource`.
## Kesimpulan
- `httpResource` menggantikan subscription `HttpClient` manual dengan satu deklarasi reaktif yang menangani loading, error, dan pembatalan secara otomatis
- Gunakan `resource()` untuk API berbasis promise, `rxResource()` untuk pipeline Observable dengan operator RxJS, dan `httpResource()` untuk panggilan HTTP standar dengan dukungan interceptor
- Angular 20 mengganti nama `request` menjadi `params` dan `loader` menjadi `stream` pada `rxResource` — perbarui kode yang ada sesuai itu
- Nilai status kini berupa literal string (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`), menggantikan enum numerik dari Angular 19
- Opsi `parse` pada `httpResource` mengintegrasikan Zod atau Valibot untuk validasi schema runtime yang juga mengarahkan tipe TypeScript
- Memanggil `value()` pada resource dalam status error melempar exception di Angular 20 — selalu lindungi dengan `hasValue()` atau periksa `status()` terlebih dahulu
- Semua varian resource membatalkan permintaan yang sedang berjalan secara otomatis ketika dependensi berubah, mencegah race condition tanpa logika pembatalan manual
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/id/blog/angular/angular-20-resource-api-httpresource-tutorial