Angular Resource API e httpResource: Guida completa (Stabile in Angular 22)
La Resource API e httpResource di Angular sono stabili da Angular 22. Questa guida copre resource(), rxResource(), httpResource(), il nuovo helper chain() per le resource dipendenti, la validazione Zod e le domande da colloquio più frequenti.

La Resource API e httpResource di Angular forniscono data fetching signal-based che traccia automaticamente gli stati di caricamento, errore e risoluzione. Introdotte in Angular 20, queste API sono diventate stabili in Angular 22, sostituendo i pattern verbosi delle sottoscrizioni HttpClient con primitive reattive.
La Resource API (resource(), rxResource(), httpResource()) è passata da sperimentale a stabile in Angular 22 (giugno 2026). La nomenclatura params e stream, gli status come stringhe letterali e il nuovo helper chain() per le resource dipendenti sono tutti pronti per la produzione. Per chi aggiorna da Angular 20 o 21, la superficie API rimane compatibile.
Le tre varianti Resource
Angular offre tre modalità per caricare dati asincroni come signal. Ogni variante si rivolge a un caso d'uso diverso, ma tutte condividono lo stesso modello reattivo: dichiarare le dipendenze, definire un loader e consumare il risultato attraverso i signal.
resource()funziona con le Promise. Ideale quando si usafetch()o qualsiasi API basata su Promise.rxResource()funziona con gli Observable. La scelta giusta quando servono operatori RxJS comedebounceTime,retryoswitchMap.httpResource()avvolge direttamenteHttpClientdi Angular. Interceptor, utility di test e validazione dello schema funzionano senza configurazioni aggiuntive.
La differenza fondamentale tra httpResource e le altre due varianti: httpResource utilizza HttpClient internamente, il che significa che gli interceptor esistenti continuano a funzionare. L'API originale resource() bypassava completamente HttpClient, una limitazione in Angular 19.
Costruire un profilo utente con resource()
La funzione resource() accetta un calcolo params e una funzione loader. Quando i signal all'interno di params cambiano, il loader viene rieseguito automaticamente.
import { Component, signal, resource } from '@angular/core';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
template: \`
@if (userResource.hasValue()) {
<h2>{{ userResource.value().name }}</h2>
<p>{{ userResource.value().email }}</p>
} @else if (userResource.isLoading()) {
<p>Loading profile...</p>
} @else if (userResource.error()) {
<p>Failed to load user</p>
}
\`,
})
export class UserProfileComponent {
userId = signal(1);
// params produces the reactive dependency
// loader receives it and returns a Promise
userResource = resource<User, number>({
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
}
}Il parametro abortSignal consente ad Angular di annullare le richieste in corso quando userId cambia prima che la richiesta precedente sia completata. Questo previene le race condition senza gestione manuale delle sottoscrizioni.
Data fetching reattivo con httpResource
httpResource elimina il codice boilerplate combinando dichiarazione URL ed esecuzione HTTP in una singola chiamata. Restituisce un HttpResourceRef che espone value, isLoading, error, status e headers come signal.
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) {
<div class="product-card">
<h3>{{ product.name }}</h3>
<span>{{ product.price | currency }}</span>
</div>
}
} @else if (products.isLoading()) {
<p>Loading products...</p>
}
\`,
})
export class ProductListComponent {
category = signal('electronics');
// httpResource re-fetches whenever category() changes
products = httpResource<Product[]>(() => ({
url: '/api/products',
params: { category: this.category() },
}));
filterByCategory(cat: string) {
this.category.set(cat); // pending request is cancelled, new one starts
}
}La funzione passata a httpResource restituisce un oggetto di configurazione della richiesta. Angular traccia le letture dei signal all'interno di questa funzione, quindi cambiare category attiva una nuova richiesta GET. Se una richiesta è già in corso, Angular la annulla prima di avviarne una nuova.
httpResource è progettato per il recupero dati (richieste GET). Utilizzarlo per operazioni POST, PUT o DELETE è rischioso perché l'annullamento delle richieste potrebbe interrompere le mutazioni a metà esecuzione. Per le operazioni di scrittura, utilizzare HttpClient direttamente o incapsulare le mutazioni in un metodo del service.
Concatenamento delle Resource con chain()
Angular 22 ha introdotto l'helper chain() per il caricamento di resource dipendenti. Quando una resource dipende dal valore di un'altra, chain() propaga automaticamente gli stati di caricamento e di errore.
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
interface User {
id: number;
name: string;
authorId: string;
}
interface Post {
id: number;
title: string;
content: string;
}
@Component({
selector: 'app-user-posts',
template: \`
@if (postsResource.isLoading()) {
<p>Loading...</p>
} @else if (postsResource.hasValue()) {
<h2>Posts by {{ userResource.value().name }}</h2>
@for (post of postsResource.value(); track post.id) {
<article>
<h3>{{ post.title }}</h3>
</article>
}
} @else if (postsResource.error()) {
<p>Failed to load posts</p>
}
\`,
})
export class UserPostsComponent {
userId = signal(1);
userResource = httpResource<User>(() => \`/api/users/\${this.userId()}\`);
// chain() propagates userResource's status to postsResource
postsResource = httpResource<Post[]>({
params: ({ chain }) => {
const user = chain(this.userResource);
return { url: '/api/posts', params: { authorId: user.authorId } };
},
});
}chain(userResource) non restituisce solo il valore. Mentre userResource è in caricamento, anche postsResource riporta lo stato di caricamento e il suo loader non viene eseguito. Se userResource va in errore, postsResource passa allo stato di errore con un ResourceDependencyError che contiene l'errore originale.
Questo sostituisce il pattern RxJS di chiamate switchMap annidate con una propagazione dello stato esplicita e corretta per default.
Validazione dello schema con Zod e httpResource
Le risposte API di servizi esterni possono divergere dalle strutture dati attese. L'opzione parse di httpResource integra librerie di validazione dello schema come Zod per intercettare le discrepanze a runtime invece di propagare silenziosamente dati corrotti.
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<typeof OrderSchema>;
@Component({
selector: 'app-order',
template: \`
@if (order.hasValue()) {
<h2>Order #{{ order.value().id }}</h2>
<p>Status: {{ order.value().status }}</p>
<p>Total: {{ order.value().total | currency }}</p>
} @else if (order.error()) {
<p>Invalid order data received</p>
}
\`,
})
export class OrderComponent {
orderId = signal(42);
// parse validates the response before exposing it as a signal
order = httpResource<Order>(
() => \`/api/orders/\${this.orderId()}\`,
{ parse: OrderSchema.parse }
);
}Quando l'API restituisce dati che non corrispondono a OrderSchema, la resource passa allo stato 'error'. Il tipo di ritorno della funzione parse determina anche il tipo TypeScript di value(), quindi le definizioni dello schema fungono sia da validatori a runtime che da generatori di tipo.
rxResource con Stream e Params
Angular rinomina loader in stream e request in params per rxResource. Questa modifica allinea la nomenclatura con la semantica di streaming che rxResource supporta. La funzione stream riceve un contesto Observable e deve restituire un Observable.
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: \`
<input (input)="query.set((.target).value)" placeholder="Search..." />
@if (results.isLoading()) {
<p>Searching...</p>
}
@if (results.hasValue()) {
@for (item of results.value(); track item.id) {
<div>{{ item.title }}</div>
}
}
\`,
})
export class SearchComponent {
private http = inject(HttpClient);
query = signal('');
// params (was "request") provides the reactive input
// stream (was "loader") returns an Observable
results = rxResource<SearchResult[], string>({
params: () => this.query(),
stream: ({ params: q }) =>
this.http.get<SearchResult[]>('/api/search', {
params: { q },
}),
});
}A differenza di httpResource, rxResource offre il controllo completo sulla pipeline Observable. Operatori come debounceTime o retry possono essere concatenati all'interno di stream. Tuttavia, per il caso più comune (singola richiesta GET), httpResource richiede meno codice.
Pronto a superare i tuoi colloqui su Angular?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Tracciamento dello stato: le stringhe letterali sostituiscono gli enum
ResourceStatus è un tipo union di stringhe. I sei valori possibili forniscono una visione granulare del ciclo di vita della resource:
| Stato | Significato |
|---|---|
'idle' | params ha restituito undefined, nessuna richiesta effettuata |
'loading' | Prima richiesta in corso |
'reloading' | Richiesta successiva dopo un precedente successo |
'resolved' | Dati disponibili in value() |
'error' | Richiesta fallita, error() contiene l'errore |
'local' | Valore impostato localmente tramite .set() o .update() |
import { Component, signal, resource } from '@angular/core';
@Component({
selector: 'app-status-demo',
template: \`
<p>Status: {{ data.status() }}</p>
@switch (data.status()) {
@case ('loading') { <spinner /> }
@case ('reloading') { <subtle-spinner /> }
@case ('resolved') { <data-table [rows]="data.value()" /> }
@case ('error') { <error-banner [error]="data.error()" /> }
@case ('idle') { <p>Select a filter to load data</p> }
}
\`,
})
export class StatusDemoComponent {
filter = signal<string | undefined>(undefined);
data = resource({
params: () => this.filter(),
loader: async ({ params: f, abortSignal }) => {
const res = await fetch(\`/api/data?filter=\${f}\`, { signal: abortSignal });
return res.json();
},
});
}Restituire undefined da params imposta lo stato su 'idle' e impedisce l'esecuzione del loader. Questo pattern funziona bene per il data fetching condizionale: mostrare un messaggio fino a quando l'utente fornisce un input, poi caricare i dati.
Chiamare value() su una resource in stato 'error' lancia un'eccezione a runtime. Le letture devono sempre essere protette con hasValue() o verificando status() prima di accedere a value(). Questa è una modifica rispetto ad Angular 19, dove value() restituiva undefined in caso di errore.
Domande da colloquio su httpResource in Angular
La Resource API sta diventando un argomento standard nelle domande da colloquio su Angular. Le seguenti domande verificano una comprensione reale che va oltre la familiarità superficiale con l'API.
D: Quale problema risolve httpResource che resource() non risolve?
resource() utilizza fetch() o qualsiasi loader basato su Promise, bypassando HttpClient di Angular. Questo significa che gli interceptor (per token di autenticazione, logging, gestione errori) non vengono applicati. httpResource utilizza HttpClient internamente, quindi interceptor, utility di test (HttpTestingController) e funzionalità come withFetch() funzionano senza configurazione aggiuntiva.
D: Quando preferire rxResource a httpResource?
rxResource offre il controllo completo degli Observable attraverso la sua funzione stream. Va scelto quando la pipeline dati richiede operatori RxJS: debouncing dell'input di ricerca, retry con backoff esponenziale o combinazione di più stream con combineLatest. Per semplici richieste GET, httpResource richiede meno codice.
D: Come gestisce Angular le richieste concorrenti quando un signal cambia rapidamente?
Tutte e tre le varianti Resource annullano le richieste in corso quando params produce un nuovo valore. Per httpResource e resource(), l'AbortSignal annulla il fetch sottostante. Per rxResource, Angular annulla la sottoscrizione all'Observable precedente. Questo impedisce che risposte obsolete sovrascrivano dati aggiornati.
D: Qual è lo scopo dello stato 'local'?
Chiamare .set() o .update() su una resource ne modifica il valore localmente senza attivare il loader. Lo stato passa a 'local', indicando che il valore corrente non proviene dal server. Questo supporta gli aggiornamenti ottimistici dell'interfaccia: l'UI riflette immediatamente la modifica mentre una richiesta di mutazione separata viene eseguita in parallelo.
D: Come funziona l'integrazione Zod con httpResource?
L'opzione parse accetta qualsiasi funzione con la firma (data: unknown) => T. Quando la risposta HTTP arriva, httpResource passa il JSON analizzato attraverso parse prima di impostare value(). Se parse lancia un errore (ad esempio ZodError), la resource passa allo stato 'error'. Il tipo di ritorno di parse determina il tipo TypeScript di value(), fornendo sia sicurezza a runtime che tipi a tempo di compilazione da un'unica definizione di schema.
D: Come gestisce chain() gli errori dalle resource a monte?
Quando si usa chain(upstreamResource) nella funzione params di una resource, la resource dipendente non esegue il suo loader se la resource a monte è in caricamento, idle o in stato di errore. Se la resource a monte va in errore, la resource dipendente passa allo stato di errore con un ResourceDependencyError. Questo errore include la dependency (la resource a monte) e cause (l'errore effettivo a monte), permettendo di distinguere tra "la mia richiesta è fallita" e "la richiesta della mia dipendenza è fallita".
Per un approfondimento su Angular Signals e la loro integrazione nel framework, la guida ai signal copre signal computati, effect e il modello di reattività.
Migrazione dalle sottoscrizioni HttpClient a httpResource
Le applicazioni Angular esistenti recuperano tipicamente i dati con sottoscrizioni HttpClient in ngOnInit o utilizzano AsyncPipe con Observable. La migrazione a httpResource segue un pattern prevedibile:
// BEFORE: manual subscription in ngOnInit
@Component({ /* ... */ })
export class BeforeComponent implements OnInit, OnDestroy {
private http = inject(HttpClient);
private destroy$ = new Subject<void>();
users: User[] = [];
loading = false;
error: string | null = null;
ngOnInit() {
this.loading = true;
this.http.get<User[]>('/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<User[]>(() => '/api/users');
// No ngOnInit, no Subject, no manual unsubscribe
// Template uses users.value(), users.isLoading(), users.error()
}La migrazione elimina tutto il boilerplate di gestione del ciclo di vita. La pulizia delle sottoscrizioni avviene automaticamente quando il componente viene distrutto. Gli stati di caricamento e di errore sono integrati nella resource, non servono flag booleane separate.
Per le applicazioni che già utilizzano Standalone Components, la migrazione è semplice: sostituire l'iniezione di HttpClient e la logica di sottoscrizione con una singola dichiarazione httpResource.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Fonti
- Documentazione Angular Resource API — guida ufficiale su resource(), rxResource() e httpResource()
- What's new in Angular 22.0 — copertura Ninja Squad della Resource API stabile e dell'helper chain()
- Guida Angular HTTP interceptors — come funzionano gli interceptor con httpResource
Punti chiave sulla Angular Resource API
httpResourcesostituisce le sottoscrizioni manuali diHttpClientcon una singola dichiarazione reattiva che gestisce caricamento, errori e annullamento automaticamente- Utilizzare
resource()per API basate su Promise,rxResource()per pipeline Observable con operatori RxJS ehttpResource()per chiamate HTTP standard con supporto agli interceptor - L'helper
chain()(Angular 22+) abilita il caricamento di resource dipendenti con propagazione automatica dello stato; se la resource a monte fallisce, quella dipendente ottiene unResourceDependencyError - I valori di stato sono stringhe letterali (
'idle','loading','resolved','error','reloading','local') - L'opzione
parsedihttpResourceintegra Zod o Valibot per la validazione dello schema a runtime che genera anche i tipi TypeScript - Chiamare
value()su una resource in stato di errore lancia un'eccezione; proteggere sempre conhasValue()o controllarestatus()prima - Tutte le varianti Resource annullano automaticamente le richieste in corso quando cambiano le dipendenze, prevenendo le race condition senza logica di annullamento manuale
Sapresti trovare il bug in Angular?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 13 ottobre 2026
Tag
Condividi
Articoli correlati

Angular Control Flow Syntax 2026: @if, @for, @switch e Domande da Colloquio
Guida completa alla nuova sintassi Control Flow di Angular con @if, @for e @switch. Ottimizzazioni delle performance, integrazione con i Signal e domande frequenti nei colloqui tecnici.

RxJS in Angular 2026: operatori, Subjects e interop con i Signals
RxJS in Angular 2026: operatori, Subjects e i pattern di interop con i Signals che gli sviluppatori Angular usano in produzione, più le domande da colloquio più frequenti.

Angular Signals e Computed nel 2026: Reattività Fine-Grained e Domande per Colloqui
Una guida approfondita ad Angular Signals, Computed Signals e al modello reattivo in Angular 20+. Include best practice, interoperabilità con RxJS e domande frequenti nei colloqui tecnici.