# 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()) {

Searching...

} @if (results.hasValue()) { @for (item of results.value(); track item.id) {
{{ item.title }}
} } `, }) 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: `

Status: {{ data.status() }}

@switch (data.status()) { @case ('loading') { } @case ('reloading') { } @case ('resolved') { } @case ('error') { } @case ('idle') {

Select a filter to load data

} } `, }) 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