Cache em TypeScript: Redis e outras ferramentas
Cache é uma das principais estratégias para otimizar o desempenho de aplicações web. No ecossistema TypeScript e Node.js, existem várias bibliotecas poderosas que permitem gerenciar o cache de dados de forma eficiente. Neste artigo, vamos detalhar as ferramentas populares, suas aplicações e as melhores práticas.
Por que usar cache?
O cache permite armazenar temporariamente dados frequentemente solicitados em um armazenamento rápido, para não precisar executar operações custosas (consultas ao banco de dados, cálculos, APIs externas) toda vez. Isso reduz a carga no servidor e diminui o tempo de resposta.
Principais ferramentas para cache em TypeScript
1. Redis através da biblioteca ioredis
Redis é um armazenamento de dados in-memory de alto desempenho que suporta várias estruturas de dados. Para TypeScript, a melhor escolha é o ioredis — um cliente com suporte completo a tipos e promises.
import Redis from 'ioredis';
// Criando o clienteconst redis = new Redis({ host: 'localhost', port: 6379,});
// Função para cache com 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);}
// Exemplo de usoasync function getUserData(userId: string) { const cacheKey = `user:${userId}`; const cached = await cacheGet(cacheKey); if (cached) { return JSON.parse(cached); } // Simulação de uma consulta cara const userData = { id: userId, name: 'John Doe' }; await cacheSet(cacheKey, JSON.stringify(userData), 3600); return userData;}2. node-cache — cache in-memory para projetos pequenos
node-cache é uma biblioteca in-memory simples que não requer dependências externas. Ideal para protótipos e microsserviços com pequeno volume de dados.
import NodeCache from 'node-cache';
const myCache = new NodeCache({ stdTTL: 600, checkperiod: 120 });
// Salvando dadosmyCache.set('key', { data: 'value' });
// Obtendo dadosconst value = myCache.get('key');if (value) { console.log('Do cache:', value);} else { console.log('Cache vazio, buscando dados...');}
// Removendo por chavemyCache.del('key');
// Limpando todo o cachemyCache.flushAll();3. async-cache-dedupe — para evitar requisições duplicadas
Esta biblioteca resolve o problema de "cache stampede" (quando várias requisições tentam preencher o cache simultaneamente). Ela automaticamente deduplica requisições paralelas para a mesma chave.
import { createCache } from 'async-cache-dedupe';
const cache = createCache({ ttl: 60, // tempo de vida em segundos stale: 10, // tempo em que os dados são considerados obsoletos, mas ainda podem ser servidos});
// Encapsulando uma função assíncronaconst getData = cache.define('getData', async (id: number) => { // Requisição cara const result = await fetch(`https://api.example.com/data/${id}`); return result.json();});
// Usandoconst result = await getData(42);Estratégias de invalidação de cache
Uma das tarefas mais complexas no cache é a invalidação — atualização de dados obsoletos. Vamos ver as principais abordagens:
- TTL (Time-To-Live) — os dados são automaticamente removidos após um tempo definido. Método simples e confiável.
- Invalidação preguiçosa — verificamos a atualidade dos dados a cada requisição (por exemplo, por versão).
- Invalidação orientada a eventos — quando os dados no banco são alterados, enviamos um evento para limpar o cache correspondente.
- Write-through — atualizamos o cache simultaneamente com a gravação no banco de dados principal.