Angular 20 en 2026: Resource API, httpResource y preguntas de entrevista
Angular 20 introduce httpResource y estabiliza la Resource API para la obtención de datos basada en signals. Un tutorial práctico sobre resource(), rxResource(), httpResource(), la validación con Zod y las preguntas de entrevista más comunes.

Angular 20 coloca la Resource API y httpResource en el centro de la obtención de datos basada en signals. Estas API experimentales reemplazan los verbosos patrones de suscripción de HttpClient por primitivas reactivas que rastrean automáticamente los estados de carga, error y resolución.
La Resource API renombra request a params y loader a stream (para rxResource). Los valores de estado ahora son literales de cadena ('idle', 'loading', 'resolved', 'error', 'reloading', 'local') en lugar de enumeraciones numéricas. httpResource se construye sobre HttpClient y admite de forma nativa los interceptors y la validación con Zod.
Las tres variantes de Resource en Angular 20
Angular 20 ofrece tres maneras de cargar datos asíncronos como signals. Cada una apunta a un caso de uso distinto, pero todas comparten el mismo modelo reactivo: declarar dependencias, definir un loader y consumir el resultado mediante signals.
resource()funciona con Promises. Ideal al usarfetch()o cualquier API basada en promesas.rxResource()funciona con Observables. La opción correcta cuando se necesitan operadores RxJS comodebounceTime,retryoswitchMap.httpResource()envuelve directamente elHttpClientde Angular. Los interceptors, las utilidades de prueba y la validación de esquemas funcionan sin cableado adicional.
La diferencia clave entre httpResource y las otras dos: httpResource usa HttpClient por debajo, lo que significa que los interceptors existentes siguen funcionando. La API resource() original evitaba por completo HttpClient, un punto débil importante en Angular 19.
Construir un perfil de usuario con resource()
La función resource() acepta una función de cálculo params y una función loader. Cuando los signals dentro de params cambian, el loader se vuelve a ejecutar automáticamente.
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
}
}El parámetro abortSignal permite que Angular cancele las solicitudes en curso cuando userId cambia antes de que la solicitud anterior termine. Esto evita condiciones de carrera sin gestión manual de las suscripciones.
Obtención reactiva de datos con httpResource
httpResource elimina el código repetitivo al combinar la declaración de la URL y la ejecución HTTP en una sola llamada. Devuelve un HttpResourceRef que expone value, isLoading, error, status y headers como 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
}
}Aquí importan varios detalles. La función pasada a httpResource devuelve un objeto de configuración de solicitud. Angular rastrea las lecturas de signals dentro de esa función, por lo que cambiar category dispara una nueva solicitud GET. Si ya hay una solicitud en curso, Angular la cancela antes de iniciar la nueva.
httpResource está diseñado para la obtención de datos (solicitudes GET). Usarlo para operaciones POST, PUT o DELETE es inseguro, porque la cancelación de la solicitud podría abortar una mutación a mitad de camino. Para operaciones de escritura, usar HttpClient directamente o envolver las mutaciones en un método de servicio.
Validación de esquemas con Zod y httpResource
Las respuestas de servicios externos pueden desviarse de la forma esperada. La opción parse de httpResource integra bibliotecas de validación de esquemas como Zod para detectar discrepancias en tiempo de ejecución, en lugar de propagar silenciosamente datos corruptos.
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 }
);
}Cuando la API devuelve datos que no coinciden con OrderSchema, la resource pasa al estado 'error'. El tipo de retorno de la función parse también determina el tipo TypeScript de value(), por lo que las definiciones de esquema cumplen una doble función: validadores en tiempo de ejecución y generadores de tipos.
rxResource con stream y params en Angular 20
Angular 20 renombra loader a stream y request a params en rxResource. Este cambio alinea la nomenclatura con la semántica de flujo que rxResource admite. La función stream recibe un contexto de Observable y debe devolver 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 },
}),
});
}A diferencia de httpResource, rxResource ofrece control total sobre el pipeline de Observable. Operadores como debounceTime o retry pueden encadenarse dentro de stream. Sin embargo, httpResource resuelve el caso más común (una sola solicitud GET) con menos código.
¿Listo para aprobar tus entrevistas de Angular?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Seguimiento del estado: los literales de cadena reemplazan a las enumeraciones
Angular 20 transforma ResourceStatus de una enumeración numérica a un tipo de unión de cadenas. Los seis valores posibles ofrecen una visión detallada del ciclo de vida de la resource:
| Estado | Significado |
|---|---|
| 'idle' | params devolvió undefined — no se emitió ninguna solicitud |
| 'loading' | Primera solicitud en curso |
| 'reloading' | Solicitud posterior tras un éxito previo |
| 'resolved' | Datos disponibles en value() |
| 'error' | La solicitud falló — error() contiene el error |
| 'local' | Valor establecido localmente mediante .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();
},
});
}Devolver undefined desde params fija el estado en 'idle' e impide que el loader se ejecute. Este patrón funciona bien para la obtención condicional de datos: mostrar un aviso hasta que el usuario proporcione una entrada y luego cargar los datos.
Desde Angular 20, llamar a value() sobre una resource en estado 'error' lanza una excepción en tiempo de ejecución. Siempre proteger las lecturas con hasValue() o comprobar status() antes de acceder a value(). Se trata de un cambio incompatible respecto a Angular 19, donde value() devolvía undefined en caso de error.
Preguntas de entrevista sobre httpResource en Angular 20
La Resource API se está convirtiendo en un tema estándar de las preguntas de entrevista de Angular. Estas preguntas evalúan una comprensión real, más allá de una familiaridad superficial con la API.
P: ¿Qué problema resuelve httpResource que resource() no resuelve?
resource() usa fetch() o cualquier loader basado en promesas, evitando el HttpClient de Angular. Esto significa que los interceptors (para tokens de autenticación, registro, manejo de errores) no se aplican. httpResource usa HttpClient internamente, por lo que los interceptors, las utilidades de prueba (HttpTestingController) y características como withFetch() funcionan sin configuración adicional.
P: ¿Cuándo conviene preferir rxResource sobre httpResource?
rxResource ofrece control total del Observable a través de su función stream. Conviene elegirlo cuando el pipeline de datos requiere operadores RxJS: aplicar debounce a la entrada de búsqueda, reintentar solicitudes fallidas con backoff exponencial o combinar varios flujos con combineLatest. Para solicitudes GET sencillas, httpResource requiere menos código.
P: ¿Cómo gestiona Angular las solicitudes concurrentes cuando un signal cambia con rapidez?
Las tres variantes de resource cancelan las solicitudes en curso cuando params produce un nuevo valor. Para httpResource y resource(), el AbortSignal cancela el fetch subyacente. Para rxResource, Angular se desuscribe del Observable anterior. Esto evita que respuestas obsoletas sobrescriban datos frescos.
P: ¿Cuál es el propósito del estado 'local'?
Llamar a .set() o .update() sobre una resource cambia su valor localmente sin disparar el loader. El estado pasa a 'local', lo que indica que el valor actual no proviene del servidor. Esto facilita las actualizaciones optimistas de la interfaz: la UI refleja el cambio de inmediato mientras se ejecuta una solicitud de mutación independiente.
P: ¿Cómo funciona la integración de Zod con httpResource?
La opción parse acepta cualquier función con la firma (data: unknown) => T. Cuando llega la respuesta HTTP, httpResource pasa el JSON analizado a través de parse antes de establecer value(). Si parse lanza una excepción (por ejemplo, ZodError), la resource pasa al estado 'error'. El tipo de retorno de parse determina el tipo TypeScript de value(), lo que aporta seguridad en tiempo de ejecución y tipos en tiempo de compilación a partir de una única definición de esquema.
Para profundizar en los signals de Angular y su integración con el framework en general, el módulo de signals cubre los computed signals, los effects y el modelo de reactividad.
Migrar de suscripciones HttpClient a httpResource
Las aplicaciones Angular existentes suelen obtener datos con suscripciones de HttpClient dentro de ngOnInit, o usan el AsyncPipe con Observables. La migración a httpResource sigue un patrón predecible:
// 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 migración elimina el código repetitivo de gestión del ciclo de vida. La limpieza de las suscripciones ocurre automáticamente cuando se destruye el componente. Los estados de carga y error están integrados en la resource: no se necesitan banderas booleanas separadas.
Para las aplicaciones que ya usan componentes standalone, la migración es directa: reemplazar la inyección de HttpClient y la lógica de suscripción por una única declaración httpResource.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Conclusión
httpResourcereemplaza las suscripciones manuales aHttpClientpor una sola declaración reactiva que gestiona automáticamente la carga, el error y la cancelación- Usar
resource()para API basadas en promesas,rxResource()para pipelines de Observable con operadores RxJS yhttpResource()para llamadas HTTP estándar con soporte de interceptors - Angular 20 renombra
requestaparamsyloaderastreamenrxResource— actualizar el código existente en consecuencia - Los valores de estado ahora son literales de cadena (
'idle','loading','resolved','error','reloading','local'), que reemplazan las enumeraciones numéricas de Angular 19 - La opción
parsedehttpResourceintegra Zod o Valibot para una validación de esquemas en tiempo de ejecución que además dirige los tipos TypeScript - Llamar a
value()sobre una resource en estado de error lanza una excepción en Angular 20 — siempre proteger conhasValue()o comprobarstatus()primero - Todas las variantes de resource cancelan automáticamente las solicitudes en curso cuando cambian las dependencias, evitando condiciones de carrera sin lógica de cancelación manual
Etiquetas
Compartir
Artículos relacionados

Componentes Standalone en Angular: Migracion desde NgModules y Buenas Practicas en 2026
Guia completa para migrar aplicaciones Angular de NgModules a componentes standalone. Incluye el proceso CLI en 3 pasos, lazy loading con loadComponent, manejo de SharedModules, pruebas unitarias, optimizacion de rendimiento y mejores practicas para desarrollo standalone-only en 2026.

Preguntas de entrevista Angular 19: Signals, SSR y conceptos imprescindibles
Las preguntas de entrevista Angular 19 más frecuentes: Signals, hidratación incremental, detección de cambios sin Zone.js y nuevas APIs reactivas con ejemplos de código y respuestas esperadas.

Angular 19 Zoneless: Rendimiento y Deteccion de Cambios Sin Zone.js
Guia completa sobre la deteccion de cambios zoneless en Angular 19, 20 y 21. Incluye configuracion de provideZonelessChangeDetection, migracion de Zone.js a Signals, SSR sin Zone.js con PendingTasks, benchmarks de rendimiento, reactive forms con toSignal y cronograma de estabilizacion hasta Angular 21.