Angular 21 de Cero a Experto / HTTP y datos
Caché: no pedir dos veces lo mismo
Pedirle al servidor la lista de categorías cada vez que se abre una pantalla es un
desperdicio: si no ha cambiado, ¿para qué volver a traerla? La CACHÉ guarda respuestas para
reutilizarlas, haciendo tu app más rápida y ahorrando peticiones. Hoy aprendes a cachear con
shareReplay, a construir un servicio de caché con expiración, y —lo más importante— a saber
cuándo invalidarla para no mostrar datos viejos.
La despensa con fecha de caducidad
Caché simple con shareReplay
Para una petición que varios lugares consumen y que no cambia seguido, shareReplay(1)
convierte el Observable en compartido y recordado —una sola petición para todos—:
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { shareReplay } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CategoriasService {
private http = inject(HttpClient);
// se pide UNA vez; todos los suscriptores reutilizan el resultado
readonly categorias$ = this.http.get<string[]>('/api/categorias').pipe(
shareReplay(1),
);
}Como el servicio es un singleton (providedIn: 'root') y shareReplay(1) recuerda el último
valor, la petición se hace la primera vez que alguien se suscribe y luego se reutiliza. Perfecto
para datos estables: categorías, configuración, listas de referencia.
Un servicio de caché con expiración
Para más control (expiración, invalidación por clave), un servicio de caché propio con un
Map:
import { Injectable } from '@angular/core';
interface Entrada<T> { valor: T; expira: number; }
@Injectable({ providedIn: 'root' })
export class CacheService {
private cache = new Map<string, Entrada<unknown>>();
get<T>(clave: string): T | null {
const entrada = this.cache.get(clave);
if (!entrada) return null;
if (Date.now() > entrada.expira) { // caducada → fuera
this.cache.delete(clave);
return null;
}
return entrada.valor as T;
}
set<T>(clave: string, valor: T, ttlMs = 60_000) {
this.cache.set(clave, { valor, expira: Date.now() + ttlMs });
}
invalidar(clave: string) { this.cache.delete(clave); }
limpiar() { this.cache.clear(); }
}Con TTL (time to live), cada entrada caduca sola tras cierto tiempo: no muestras datos eternamente viejos. El servicio consulta la caché antes de pedir, y guarda la respuesta.
El problema difícil: invalidar
Pruébalo: una caché con TTL, en vivo
La lógica de una caché —guardar con expiración, servir si está fresca, invalidar— es pura. Aquí, funcionando:
Observa: la 1ª lectura pide al servidor y cachea; la 2ª y 3ª sirven de la caché (sin petición).
Cuando el usuario crea un gasto, INVALIDAMOS la clave, así la 4ª lectura vuelve a pedir datos
frescos. Sin ese invalidar, seguiríamos mostrando la lista vieja —el bug clásico de caché—.
Las stats muestran cuántas peticiones te ahorró la caché.
Cacheas la lista de gastos. El usuario crea un gasto nuevo (POST). ¿Qué debes hacer para que no vea datos viejos?
Mini-reto
En el playground: 1) agrega un método obtenerCategorias() con su propia clave y un TTL más
largo (categorías cambian poco); 2) simula que el usuario edita un gasto e invalida solo
'gastos' (no las categorías); 3) verifica con las stats que las categorías siguen sirviendo
de caché mientras los gastos se repidieron. Piensa qué TTL le pondrías a datos que casi nunca
cambian vs a datos muy dinámicos.
Qué sigue
Manejas peticiones, estados, interceptores y caché a mano. Angular moderno empaqueta muchos de
estos patrones en una sola herramienta declarativa: httpResource. La próxima lección la
presenta —cargar datos con estados y recarga reactiva en pocas líneas—, y cierra la teoría del
módulo antes del reto.