Caching en TypeScript: Redis y otras herramientas
El caching es una de las estrategias clave para optimizar el rendimiento de las aplicaciones web. En el ecosistema de TypeScript y Node.js, existen varias bibliotecas potentes que permiten gestionar eficazmente el caching de datos. En este artículo, analizaremos en detalle las herramientas populares, su aplicación y las mejores prácticas.
¿Por qué es necesario el caching?
El caching permite almacenar temporalmente datos solicitados con frecuencia en un almacenamiento rápido, para no tener que realizar operaciones costosas (consultas a la base de datos, cálculos, APIs externas) cada vez. Esto reduce la carga en el servidor y disminuye el tiempo de respuesta.
Herramientas principales para caching en TypeScript
1. Redis a través de la biblioteca ioredis
Redis es un almacén de datos en memoria de alto rendimiento que admite varias estructuras de datos. Para TypeScript, la mejor opción es ioredis, un cliente con soporte completo de tipos y promesas.
import Redis from 'ioredis';
// Creamos el clienteconst redis = new Redis({ host: 'localhost', port: 6379,});
// Función para caching con TTLasync function cacheGet(key: string): Promise<string | null> { const data = await redis.get(key); return data;}
async function cacheSet(key: string, value: string, ttl: number = 3600): Promise<void> { await redis.setex(key, ttl, value);}
// Ejemplo de usoasync function getUserData(userId: string) { const cacheKey = `user:${userId}`; const cached = await cacheGet(cacheKey); if (cached) { return JSON.parse(cached); } // Simulación de una consulta costosa const userData = { id: userId, name: 'John Doe' }; await cacheSet(cacheKey, JSON.stringify(userData), 3600); return userData;}2. node-cache — caché en memoria para proyectos pequeños
node-cache es una biblioteca simple en memoria que no requiere dependencias externas. Es ideal para prototipos y microservicios con volúmenes de datos pequeños.
import NodeCache from 'node-cache';
const myCache = new NodeCache({ stdTTL: 600, checkperiod: 120 });
// Guardamos datosmyCache.set('key', { data: 'value' });
// Obtenemos datosconst value = myCache.get('key');if (value) { console.log('Desde caché:', value);} else { console.log('Caché vacío, solicitando datos...');}
// Eliminación por clavemyCache.del('key');
// Limpiar toda la cachémyCache.flushAll();3. async-cache-dedupe — para evitar solicitudes duplicadas
Esta biblioteca resuelve el problema de "cache stampede" (cuando múltiples solicitudes intentan llenar la caché simultáneamente). Deduplica automáticamente las solicitudes paralelas a la misma clave.
import { createCache } from 'async-cache-dedupe';
const cache = createCache({ ttl: 60, // tiempo de vida en segundos stale: 10, // tiempo durante el cual los datos se consideran obsoletos pero aún se pueden servir});
// Envolvemos una función asíncronaconst getData = cache.define('getData', async (id: number) => { // Consulta costosa const result = await fetch(`https://api.example.com/data/${id}`); return result.json();});
// Usamosconst result = await getData(42);Estrategias de invalidación de caché
Uno de los desafíos más complejos en el caching es la invalidación: la actualización de datos obsoletos. Analicemos los enfoques principales:
- TTL (Time-To-Live) — los datos se eliminan automáticamente después de un tiempo determinado. Método simple y fiable.
- Invalidación perezosa — verificamos la vigencia de los datos en cada solicitud (por ejemplo, mediante una versión).
- Invalidación basada en eventos — al modificar datos en la base de datos, enviamos un evento para limpiar la caché correspondiente.
- Write-through — actualizamos la caché simultáneamente con la escritura en la base de datos principal.
También en la biblioteca
Procesamiento de imágenes en Node.js: bibliotecas Sharp, Jimp y gm
Generador de códigos QR en Go (Golang) con la librería go-qrcode
Caché en C++: biblioteca Cache++
Serialización de objetos en TypeScript: bibliotecas, métodos y ejemplos