Angular Asíncrono: ¿Cómo httpResource Cambia las Reglas del Juego?

Publicado por David Filipe Lopes Domingues el

FrontendAngularAsíncroniaProgramación Reactiva

En los últimos dos años, Angular ha dado un paso importante en la evolución de su modelo reactivo, introduciendo los Signals como elementos fundamentales para la reactividad síncrona. Pero esta transformación no se quedó ahí, en la versión 19, Angular reforzó su enfoque en la reactividad asíncrona, especialmente en el manejo de las solicitudes HTTP, introduciendo una nueva API denominada Resource API.

Fuente: https://www.angular.courses/caniuse
En la versión 20 de Angular, la nueva API de Resource sigue siendo experimental. Esto implica que la forma de usar esta funcionalidad podrá modificarse antes de volverse estable.

Esta API simplifica la gestión de datos asíncronos usando Signals para controlar automáticamente las solicitudes, el estado de carga y la actualización de datos. Con la llegada de httpResource, Angular ofrece una forma aún más eficiente y escalable para manejar peticiones HTTP.

Una nueva arquitectura

Tradicionalmente, se utilizaban Observables y operadores como map, switchMap, catchError, tap, retryWhen y otros para manejar flujos de datos asincrónicos. Sin embargo, con la introducción de Signals y las API Resource, ahora tenemos una forma más declarativa, reactiva y legible de gestionar solicitudes HTTP.

¿Qué hace httpResource() mejor que resource() o rxResource()?

comparación de una solicitud GET usando cada una de las funciones

Las tres implementaciones son reactivas al Signal limit, activando una nueva solicitud HTTP cuando este cambia. Además, todas exponen un objeto ResourceRef, que incluye señales para el valor actual, el estado de carga, los errores y una función reload() para volver a cargar manualmente los datos.

  • resource es útil para cargas simples basadas en promesas, pero no se integra con el HttpClient, lo que limita su uso en aplicaciones Angular complejas. Podría ser útil para descarga de recursos del servidor donde está hospedada la aplicación frontend, por ejemplo: fetch('my-app-url/public/i18n_es.json').
  • rxResource permite usar Observables, integrándose con HttpClient y RxJS, pero requiere más configuración manual y manejo explícito del flujo de datos. Útil en casos en donde las utilidades de RxJS, como sus operadores, pueden ser necesarios para controlar el flujo de datos, por ejemplo, usando forkJoin para solicitar múltiples recursos en paralelo y combinarlos en una sola respuesta.
  • httpResource es la opción más completa y declarativa, que se integra con HttpClient y permite configurar headers, parámetros y body. Su reactividad es más simple, porque detecta automáticamente los Signals usados dentro de la función que devuelve el objeto HttpResourceRequest. Esta función puede exportarse desde un fichero para reutilizar fácilmente la lógica de cada endpoint.

Integración de httpResource con HttpClient y Interceptors

Para usar httpResource, es necesario proporcionar HttpClient con provideHttpClient, lo que permite aplicar interceptores como el que añade el header x-api-key a todas las peticiones.


Ejemplo práctico

Este ejemplo desarrollado en Angular 20 permite consultar imágenes de gatos desde una API, indicando cuántas mostrar. Sirve para comparar dos enfoques: uno clásico con Observables y otro moderno con Signals.

Requisitos funcionales:

  • Funcionalidad: Consultar imágenes de gatos desde The CatAPI.
  • Parámetro configurable: Número de imágenes a obtener, introducido por el usuario.
  • Recarga manual: Botón para volver a ejecutar la petición con el valor actual.
  • Gestión de estados:
    • Mostrar un mensaje de “CARGANDO...” mientras se realiza la petición.
    • Mostrar un mensaje de “ERROR...” si ocurre algún error.
    • Mostrar las imágenes si la respuesta es exitosa.

Arquitectura Tradicional con Observables

Una solución común es utilizar un FormControl para capturar el valor del input y suscribirse a los cambios mediante valueChanges. Al cambiar el valor, se emplea switchMap para cancelar peticiones anteriores y realizar una nueva. El estado de carga (isLoading) y el error (hasError) se gestionan manualmente. En la plantilla, se usa AsyncPipe para suscribirse al observable cats$.

Este componente requiere la inyección de un servicio CatsApiService que usa HttpClient para hacer peticiones a la API. Recibe un parámetro limit y devuelve un observable con una lista de imágenes. Si el limit es nulo, devuelve un observable con valor null.

Desventajas de esta versión:

  • Verboso y propenso a errores si no se manejan correctamente los estados.
  • El control de carga y del error es imperativo y disperso.
  • Requiere múltiples operadores RxJS para tareas simples.
  • Implica en la inyección de la dependencia de CatsApiService en el componente y de HttpClient en el servicio.

Arquitectura Moderna con Signals

Una posible implementación con Signals seria usar la primitiva signal() para crear un estado reactivo con [(ngModel)], mientras que httpResource()
transforma la petición HTTP en un HttpResourceRef que el template puede consultar directamente mediante catsResource.value(), catsResource.isLoading() y catsResource.error().

En Angular 20, la nueva guía de buenas prácticas recomienda unificar el nombre de los archivos de un componente, eliminando sufijos como .component.ts o .service.ts. Por ejemplo, en lugar de user-profile.component.ts, se usa simplemente user-profile.ts, compartiendo el mismo nombre base con su HTML y CSS.

Por otro lado, el archivo cats-api.ts centraliza funciones puras con la lógica de las peticiones HTTP, permitiendo su reutilización desde distintas partes de la aplicación, algo habitual en proyectos de gran escala.

  • Más simple y legible: No necesitas operadores RxJS para casos comunes.
  • Estado centralizado: La carga (isLoading) y el error (error) están encapsulados en el resource.
  • Más declarativo: Es fácil de razonar y más cercano a la forma moderna de construir UI reactivas.
  • Reactivo y cancelable: Al ser usado con Signal, los cambios en limit reactivan la petición automáticamente.
  • Mejor integración con zoneless: No depende de zonas, mejora el rendimiento al renderizar los nodos con
  • Más funcional: no implica la utilización de dependencias enfocándose en en un paradigma más funcional.

Más allá de las solicitudes sencillas

Además de las peticiones GET, para obtener datos. En esta sección, nos centraremos en el uso de POST para marcar imágenes como favoritas y DELETE para eliminarlas de la lista de favoritos, utilizando el id de cada imagen.

Requisitos funcionales:

  • Funcionalidad: Poder agregar y eliminar imágenes de favoritas.
  • Agregar favorito: Botón en la base de cada foto del listado normal para agregar favorito
  • Remover favorito: Botón para eliminar imagen de listado de favoritos.
  • Gestión de estados:
    • Mostrar un mensaje de “CARGANDO...” mientras se realiza la petición.
    • Mostrar un mensaje de “ERROR...” si ocurre algún error.
    • Recarga de favoritos si se agrega o se elimina una imagen de favoritos

httpResource para peticiones POST y DELETE

Se han agregado al componente el Signal favouriteId y el ResourceRef addFavouriteRes para poder agregar favoritos reactivamente siempre que se hace favouriteId.set(cat.id).

postFavourites recibe un Signal con el ID y devuelve otra función que retorna un objeto HttpResourceRequest para hacer una petición POST y añadir esa ID a favoritos. Si el ID es válido, la petición incluye el método, la URL y el body con el image_id y sub_id. Si el ID es null, no se hace ninguna petición.

Veamos ahora como remover de favoritos, para ello se presenta el siguiente componente:

Cuando este componente se renderiza el fetchFavouriteResource se encarga de obtener el listado de imágenes favoritas y el Signal idToDelete permite eliminarlos al invocar la función set idToDelete.set(cat.id).

Propiedades principales:

  • fetchFavouriteResource: Es un httpResource que realiza una petición GET a la API de TheCatAPI para obtener la lista de gatos favoritos. Su valor es reactivo y puede ser recargado manualmente con .reload().
  • idToDelete: Es un Signal que mantiene el ID del gato a eliminar. Se actualiza al hacer clic en el botón "X" de cada imagen.
  • deleteFavouriteResource: Es otro httpResource, configurado con una función deleteFavourite que toma el ID del gato como parámetro y genera la petición DELETE correspondiente.
  • loading: Es un computed que combina los estados de carga de ambas peticiones (fetchFavouriteResource y deleteFavouriteResource) para mostrar un mensaje mientras se realiza cualquier operación.
  • eff: Es un effect que observa el estado de deleteFavouriteResource. Cada vez que termina una eliminación, actualiza la lista de favoritos mediante fetchFavouriteResource.reload().

Conclusiones

La evolución del modelo reactivo en Angular con la introducción de Signals y la Resource API marca un cambio significativo en la forma en que se construyen aplicaciones modernas. La gestión declarativa de peticiones HTTP mediante httpResource() simplifica el desarrollo, reduce la verbosidad del código y mejora la legibilidad y escalabilidad, especialmente en comparación con la arquitectura tradicional basada en Observables y operadores RxJS.

En particular, httpResource() destaca por:

  • Su integración directa con HttpClient e interceptores.
  • El uso automático y reactivo de Signals para activar solicitudes.
  • La exposición de estados clave como isLoading, error y reload() de forma centralizada y declarativa.

Además, permite gestionar no solo peticiones GET, sino también POST y DELETE, manteniendo la reactividad y coherencia del estado de la UI sin necesidad de código imperativo.

Aunque esta API todavía es experimental en Angular 20, ya representa una solución poderosa y moderna para la gestión de datos asincrónicos, especialmente en aplicaciones complejas o enfocadas en rendimiento (como aquellas sin zonas).

En resumen, la combinación de Signals y httpResource() impulsa una arquitectura más limpia, funcional y reactiva, acercando a Angular a las prácticas más modernas del desarrollo frontend.