# Angular 20 nel 2026: Resource API, httpResource e domande da colloquio > Angular 20 introduce la Resource API e httpResource come alternative signal-based alle sottoscrizioni manuali di HttpClient. Questa guida copre tutte e tre le varianti Resource, la validazione con Zod e le domande più frequenti nei colloqui tecnici. - Published: 2026-05-30 - Updated: 2026-05-30 - Author: SharpSkill - Tags: angular, typescript, resource-api, httpresource, signals - Reading time: 12 min --- Angular 20 porta la [Resource API](https://angular.dev/guide/signals/resource) e `httpResource` al centro del data fetching basato su signal. Queste API sostituiscono i pattern verbosi delle sottoscrizioni `HttpClient` con primitive reattive che tracciano automaticamente gli stati di caricamento, errore e risoluzione. > **Cosa cambia in Angular 20** > > La Resource API rinomina `request` in `params` e `loader` in `stream` (per `rxResource`). I valori di stato sono ora stringhe letterali (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`) invece di enum numerici. `httpResource` si basa su `HttpClient`, supportando interceptor e validazione Zod senza configurazioni aggiuntive. ## Le tre varianti Resource in Angular 20 Angular 20 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 usa `fetch()` o qualsiasi API basata su Promise. - **`rxResource()`** funziona con gli Observable. La scelta giusta quando servono operatori RxJS come `debounceTime`, `retry` o `switchMap`. - **`httpResource()`** avvolge direttamente `HttpClient` di 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](https://angular.dev/guide/http/interceptors) esistenti continuano a funzionare. L'API originale `resource()` bypassava completamente `HttpClient`, un punto dolente significativo 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. ```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 } } ``` 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. ```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 } } ``` Alcuni dettagli sono importanti. 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 è solo per la lettura** > > `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. ## 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](https://zod.dev/) per intercettare le discrepanze a runtime invece di propagare silenziosamente dati corrotti. ```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 } ); } ``` 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 in Angular 20 Angular 20 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. ```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 }, }), }); } ``` 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. ## Tracciamento dello stato: le stringhe letterali sostituiscono gli enum Angular 20 cambia `ResourceStatus` da un enum numerico a 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()` | ```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(); }, }); } ``` 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. > **Accesso a value() nello stato di errore** > > A partire da Angular 20, 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 breaking change rispetto ad Angular 19, dove `value()` restituiva `undefined` in caso di errore. ## Domande da colloquio su httpResource in Angular 20 La Resource API sta diventando un argomento standard nelle [domande da colloquio su Angular](/blog/angular/angular-19-interview-questions-signals-ssr-must-know). 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. Per un approfondimento su [Angular Signals](/technologies/angular/interview-questions/angular-signals) e la loro integrazione nel framework, il modulo sui 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: ```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() } ``` 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](/blog/angular/angular-standalone-components-migration-best-practices), la migrazione è semplice: sostituire l'iniezione di `HttpClient` e la logica di sottoscrizione con una singola dichiarazione `httpResource`. ## Conclusione - `httpResource` sostituisce le sottoscrizioni manuali di `HttpClient` con 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 e `httpResource()` per chiamate HTTP standard con supporto agli interceptor - Angular 20 rinomina `request` in `params` e `loader` in `stream` per `rxResource` — aggiornare il codice esistente di conseguenza - I valori di stato sono ora stringhe letterali (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`), sostituendo gli enum numerici di Angular 19 - L'opzione `parse` di `httpResource` integra 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 in Angular 20 — proteggere sempre con `hasValue()` o controllare `status()` prima - Tutte le varianti Resource annullano automaticamente le richieste in corso quando cambiano le dipendenze, prevenendo le race condition senza logica di annullamento manuale --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/angular/angular-20-resource-api-httpresource-tutorial