Angular 20 en 2026 : Resource API, httpResource et questions d'entretien
Angular 20 introduit httpResource et stabilise la Resource API pour la récupération de données fondée sur les signals. Un tutoriel pratique sur resource(), rxResource(), httpResource(), la validation Zod et les questions d'entretien courantes.

Angular 20 place la Resource API et httpResource au cœur de la récupération de données fondée sur les signals. Ces API expérimentales remplacent les schémas de souscription verbeux de HttpClient par des primitives réactives qui suivent automatiquement les états de chargement, d'erreur et de résolution.
La Resource API renomme request en params et loader en stream (pour rxResource). Les valeurs de statut sont désormais des littéraux de chaîne ('idle', 'loading', 'resolved', 'error', 'reloading', 'local') au lieu d'énumérations numériques. httpResource s'appuie sur HttpClient et prend en charge nativement les interceptors et la validation Zod.
Les trois variantes de Resource dans Angular 20
Angular 20 propose trois manières de charger des données asynchrones sous forme de signals. Chacune cible un cas d'usage différent, mais toutes partagent le même modèle réactif : déclarer les dépendances, définir un loader, puis consommer le résultat via des signals.
resource()fonctionne avec les Promises. Idéal pourfetch()ou toute API fondée sur les promesses.rxResource()fonctionne avec les Observables. Le bon choix lorsque des opérateurs RxJS commedebounceTime,retryouswitchMapsont nécessaires.httpResource()encapsule directement leHttpClientd'Angular. Les interceptors, les utilitaires de test et la validation de schéma fonctionnent sans configuration supplémentaire.
La différence essentielle entre httpResource et les deux autres : httpResource s'appuie sur HttpClient en interne, ce qui signifie que les interceptors existants continuent de fonctionner. L'API resource() d'origine contournait totalement HttpClient, un point de friction majeur dans Angular 19.
Construire un profil utilisateur avec resource()
La fonction resource() accepte une fonction de calcul params et une fonction loader. Lorsque les signals présents dans params changent, le loader se réexécute automatiquement.
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
}
}Le paramètre abortSignal permet à Angular d'annuler les requêtes en cours lorsque userId change avant que la requête précédente ne se termine. Cela évite les conditions de course sans gestion manuelle des souscriptions.
Récupération réactive de données avec httpResource
httpResource élimine le code répétitif en combinant la déclaration de l'URL et l'exécution HTTP en un seul appel. Il retourne un HttpResourceRef qui expose value, isLoading, error, status et headers sous forme de signals.
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
}
}Plusieurs détails comptent ici. La fonction passée à httpResource retourne un objet de configuration de requête. Angular suit les lectures de signals à l'intérieur de cette fonction : modifier category déclenche donc une nouvelle requête GET. Si une requête est déjà en cours, Angular l'annule avant de lancer la nouvelle.
httpResource est conçu pour la récupération de données (requêtes GET). L'utiliser pour des opérations POST, PUT ou DELETE est dangereux, car l'annulation d'une requête pourrait interrompre une mutation en cours. Pour les opérations d'écriture, utiliser directement HttpClient ou encapsuler les mutations dans une méthode de service.
Validation de schéma avec Zod et httpResource
Les réponses des services externes peuvent dévier de la forme attendue. L'option parse de httpResource intègre des bibliothèques de validation de schéma comme Zod afin de détecter les écarts à l'exécution, au lieu de propager silencieusement des données corrompues.
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 }
);
}Lorsque l'API retourne des données qui ne correspondent pas à OrderSchema, la resource passe au statut 'error'. Le type de retour de la fonction parse détermine également le type TypeScript de value() : les définitions de schéma jouent donc un double rôle, validateurs à l'exécution et générateurs de types.
rxResource avec stream et params dans Angular 20
Angular 20 renomme loader en stream et request en params dans rxResource. Ce changement aligne la nomenclature sur la sémantique de flux que rxResource prend en charge. La fonction stream reçoit un contexte d'Observable et doit retourner 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($any($event.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 },
}),
});
}Contrairement à httpResource, rxResource offre un contrôle total sur le pipeline d'Observable. Des opérateurs comme debounceTime ou retry peuvent être chaînés à l'intérieur de stream. En revanche, httpResource traite le cas le plus courant (une seule requête GET) avec moins de code.
Prêt à réussir tes entretiens Angular ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Suivi du statut : les littéraux de chaîne remplacent les énumérations
Angular 20 transforme ResourceStatus, qui passe d'une énumération numérique à un type union de chaînes. Les six valeurs possibles offrent une vision fine du cycle de vie de la resource :
| Statut | Signification |
|---|---|
| 'idle' | params a retourné undefined — aucune requête émise |
| 'loading' | Première requête en cours |
| 'reloading' | Requête suivante après un succès précédent |
| 'resolved' | Données disponibles dans value() |
| 'error' | Échec de la requête — error() contient l'erreur |
| 'local' | Valeur définie localement via .set() ou .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();
},
});
}Retourner undefined depuis params fixe le statut à 'idle' et empêche le loader de s'exécuter. Ce schéma convient bien à la récupération conditionnelle de données : afficher une invite tant que l'utilisateur n'a pas fourni d'entrée, puis charger les données.
Depuis Angular 20, appeler value() sur une resource au statut 'error' lève une exception à l'exécution. Toujours protéger les lectures avec hasValue() ou vérifier status() avant d'accéder à value(). Il s'agit d'un changement cassant par rapport à Angular 19, où value() retournait undefined en cas d'erreur.
Questions d'entretien Angular 20 sur httpResource
La Resource API devient un sujet incontournable des questions d'entretien Angular. Voici des questions qui testent une compréhension réelle, au-delà d'une familiarité superficielle avec l'API.
Q : Quel problème httpResource résout-il que resource() ne résout pas ?
resource() utilise fetch() ou tout loader fondé sur une promesse, en contournant le HttpClient d'Angular. Les interceptors (jetons d'authentification, journalisation, gestion d'erreurs) ne s'appliquent donc pas. httpResource utilise HttpClient en interne : les interceptors, les utilitaires de test (HttpTestingController) et des fonctionnalités comme withFetch() fonctionnent sans configuration supplémentaire.
Q : Quand faut-il préférer rxResource à httpResource ?
rxResource offre un contrôle total sur l'Observable via sa fonction stream. Le choisir lorsque le pipeline de données nécessite des opérateurs RxJS : limiter une saisie de recherche, réessayer des requêtes échouées avec un backoff exponentiel, ou combiner plusieurs flux avec combineLatest. Pour de simples requêtes GET, httpResource demande moins de code.
Q : Comment Angular gère-t-il les requêtes concurrentes lorsqu'un signal change rapidement ?
Les trois variantes de resource annulent les requêtes en cours lorsque params produit une nouvelle valeur. Pour httpResource et resource(), l'AbortSignal annule le fetch sous-jacent. Pour rxResource, Angular se désabonne de l'Observable précédent. Cela empêche des réponses obsolètes d'écraser des données fraîches.
Q : À quoi sert le statut 'local' ?
Appeler .set() ou .update() sur une resource modifie sa valeur localement sans déclencher le loader. Le statut passe à 'local', ce qui indique que la valeur courante ne provient pas du serveur. Cela facilite les mises à jour optimistes de l'interface : l'UI reflète le changement immédiatement pendant qu'une requête de mutation distincte s'exécute.
Q : Comment fonctionne l'intégration de Zod avec httpResource ?
L'option parse accepte toute fonction de signature (data: unknown) => T. À l'arrivée de la réponse HTTP, httpResource passe le JSON analysé à travers parse avant de définir value(). Si parse lève une exception (par exemple ZodError), la resource passe au statut 'error'. Le type de retour de parse détermine le type TypeScript de value(), offrant à la fois sécurité à l'exécution et types à la compilation à partir d'une seule définition de schéma.
Pour approfondir les signals Angular et leur intégration dans le framework au sens large, le module sur les signals couvre les computed signals, les effects et le modèle de réactivité.
Migrer des souscriptions HttpClient vers httpResource
Les applications Angular existantes récupèrent généralement des données via des souscriptions HttpClient dans ngOnInit, ou utilisent l'AsyncPipe avec des Observables. La migration vers httpResource suit un schéma prévisible :
// 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 migration supprime le code répétitif de gestion du cycle de vie. Le nettoyage des souscriptions se fait automatiquement à la destruction du composant. Les états de chargement et d'erreur sont intégrés à la resource : plus besoin de drapeaux booléens séparés.
Pour les applications utilisant déjà les composants standalone, la migration est directe : remplacer l'injection de HttpClient et la logique de souscription par une unique déclaration httpResource.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Conclusion
httpResourceremplace les souscriptions manuelles àHttpClientpar une seule déclaration réactive qui gère automatiquement le chargement, l'erreur et l'annulation- Utiliser
resource()pour les API fondées sur les promesses,rxResource()pour les pipelines d'Observables avec opérateurs RxJS, ethttpResource()pour les appels HTTP standard avec prise en charge des interceptors - Angular 20 renomme
requestenparamsetloaderenstreamdansrxResource— mettre à jour le code existant en conséquence - Les valeurs de statut sont désormais des littéraux de chaîne (
'idle','loading','resolved','error','reloading','local'), remplaçant les énumérations numériques d'Angular 19 - L'option
parsedehttpResourceintègre Zod ou Valibot pour une validation de schéma à l'exécution qui pilote aussi les types TypeScript - Appeler
value()sur une resource en état d'erreur lève une exception dans Angular 20 — toujours protéger avechasValue()ou vérifierstatus()d'abord - Toutes les variantes de resource annulent automatiquement les requêtes en cours lorsque les dépendances changent, évitant les conditions de course sans logique d'annulation manuelle
Tags
Partager
Articles similaires

Angular Standalone Components : Migration et Bonnes Pratiques en 2026
Guide complet pour migrer les applications Angular des NgModules vers les standalone components. Processus de migration CLI en 3 etapes, lazy loading, routage, tests et bonnes pratiques pour Angular 21.

Angular 19 en entretien : Signals, SSR et les questions incontournables
Les questions d'entretien Angular 19 les plus fréquentes : Signals, SSR avec hydratation incrémentale, détection de changement zoneless et nouvelles API réactives.

Angular 19 Zoneless : Detection de Changements sans Zone.js et Gains de Performance
Guide complet sur la detection de changements sans Zone.js dans Angular 19 et 20. Configuration de provideZonelessChangeDetection, migration vers les signals, SSR sans Zone.js, benchmarks de performance et pieges a eviter lors de la migration.