# 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. - Published: 2026-05-30 - Updated: 2026-06-06 - Author: SharpSkill - Tags: angular, angular-20, resource-api, httpresource, signals, typescript, tutorial - Reading time: 10 min --- Angular 20 coloca la [Resource API](https://angular.dev/guide/signals/resource) 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. > **Qué cambia en Angular 20** > > 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 usar `fetch()` o cualquier API basada en promesas. - **`rxResource()`** funciona con Observables. La opción correcta cuando se necesitan operadores RxJS como `debounceTime`, `retry` o `switchMap`. - **`httpResource()`** envuelve directamente el `HttpClient` de 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](https://angular.dev/guide/http/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. ```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 } } ``` 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. ```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 } } ``` 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 es solo para lecturas** > > `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](https://zod.dev/) para detectar discrepancias en tiempo de ejecución, en lugar de propagar silenciosamente datos corruptos. ```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 } ); } ``` 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. ```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 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. ## 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()` | ```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(); }, }); } ``` 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. > **Acceder a value() en estado de error** > > 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](/blog/angular/angular-19-interview-questions-signals-ssr-must-know). 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](/technologies/angular/interview-questions/angular-signals) 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: ```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 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](/blog/angular/angular-standalone-components-migration-best-practices), la migración es directa: reemplazar la inyección de `HttpClient` y la lógica de suscripción por una única declaración `httpResource`. ## Conclusión - `httpResource` reemplaza las suscripciones manuales a `HttpClient` por 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 y `httpResource()` para llamadas HTTP estándar con soporte de interceptors - Angular 20 renombra `request` a `params` y `loader` a `stream` en `rxResource` — 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 `parse` de `httpResource` integra 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 con `hasValue()` o comprobar `status()` 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/angular/angular-20-resource-api-httpresource-tutorial