# 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()) {
}
}
`,
})
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: `
}
}
`,
})
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