<h2>Redis e Node.js: Fundamentos Essenciais</h2>
<p>Redis é um banco de dados em memória (in-memory data store) que se tornou essencial em arquiteturas modernas. Diferentemente de bancos tradicionais, Redis opera na RAM, oferecendo latência extremamente baixa—na ordem de milissegundos. Com Node.js, você integra Redis através da biblioteca <code>redis</code> (cliente oficial) ou <code>ioredis</code> (alternativa com recursos adicionais). Neste artigo, exploraremos os quatro pilares: caching, pub/sub, filas e session store.</p>
<p>Antes de começar, instale as dependências necessárias:</p>
<pre><code class="language-bash">npm install redis express express-session connect-redis uuid</code></pre>
<h2>Cache com Redis</h2>
<p>O caching reduz requisições ao banco de dados, acelerando drasticamente sua aplicação. Redis armazena pares chave-valor simples ou estruturas complexas (hashes, listas, sets).</p>
<h3>Implementação Básica de Cache</h3>
<pre><code class="language-javascript">const redis = require('redis');
const express = require('express');
const app = express();
const client = redis.createClient({
host: 'localhost',
port: 6379
});
client.connect();
// Simular consulta ao banco pesada
async function buscarUsuario(id) {
// Verificar se existe em cache
const cached = await client.get(user:${id});
if (cached) {
console.log('Cache hit');
return JSON.parse(cached);
}
// Simular query ao BD
const usuario = { id, nome: 'João Silva', email: 'joao@example.com' };
// Armazenar no cache por 1 hora (3600 segundos)
await client.setEx(user:${id}, 3600, JSON.stringify(usuario));
console.log('Cache miss - dados armazenados');
return usuario;
}
app.get('/usuario/:id', async (req, res) => {
const usuario = await buscarUsuario(req.params.id);
res.json(usuario);
});</code></pre>
<p>A função <code>setEx</code> define tanto o valor quanto a expiração em uma única operação. Isso é fundamental para evitar que dados obsoletos permaneçam em cache indefinidamente. Use chaves estruturadas (<code>user:123</code>) para organizar dados logicamente.</p>
<h2>Pub/Sub: Comunicação em Tempo Real</h2>
<p>Pub/Sub (Publish/Subscribe) permite que múltiplos clientes se inscrevam em canais e recebam mensagens em tempo real. É ideal para notificações, atualizações ao vivo e coordenação entre serviços.</p>
<h3>Sistema de Notificações em Tempo Real</h3>
<pre><code class="language-javascript">const redis = require('redis');
// Subscriber (ouvinte)
async function iniciarSubscriber() {
const subscriber = redis.createClient();
await subscriber.connect();
subscriber.subscribe('notificacoes', (mensagem) => {
console.log('Notificação recebida:', mensagem);
});
}
// Publisher (emissor)
async function enviarNotificacao(texto) {
const publisher = redis.createClient();
await publisher.connect();
await publisher.publish('notificacoes', texto);
await publisher.quit();
}
// Em uma rota Express
app.post('/notificar', async (req, res) => {
await enviarNotificacao(Novo pedido recebido: ${req.body.pedidoId});
res.json({ sucesso: true });
});
iniciarSubscriber();</code></pre>
<p>Cada subscriber recebe apenas mensagens publicadas <strong>após</strong> sua inscrição. Para persistência, considere usar Redis Streams. Pub/Sub é perfeito para coordenar eventos entre microserviços ou notificar múltiplos clientes WebSocket simultaneamente.</p>
<h2>Filas com Redis</h2>
<p>Filas em Redis processam tarefas assincronamente, desacoplando produtores de consumidores. Use listas Redis (operações <code>LPUSH</code> e <code>RPOP</code>) para implementar padrões FIFO robustos.</p>
<h3>Fila de Processamento de Emails</h3>
<pre><code class="language-javascript">const redis = require('redis');
const client = redis.createClient();
client.connect();
// Produtor: adiciona tarefas à fila
async function adicionarEmailParaEnviar(para, assunto) {
const tarefa = JSON.stringify({ para, assunto, timestamp: Date.now() });
await client.lPush('fila:emails', tarefa);
console.log('Email adicionado à fila');
}
// Consumidor: processa emails
async function processarFilaEmails() {
while (true) {
// BRPOP bloqueia até haver item (timeout de 0 = espera indefinida)
const tarefa = await client.brPop('fila:emails', 0);
if (tarefa) {
const { para, assunto } = JSON.parse(tarefa.element);
console.log(Enviando email para ${para}: ${assunto});
// Chamar serviço de email real aqui
// Simular processamento
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
// Em uma rota
app.post('/enviar-email', async (req, res) => {
await adicionarEmailParaEnviar(req.body.para, req.body.assunto);
res.json({ mensagem: 'Email enfileirado' });
});
// Inicie o processador em background
processarFilaEmails().catch(console.error);</code></pre>
<p>Use <code>BRPOP</code> para esperar bloqueante em vez de polling contínuo. Isso economiza CPU e oferece latência previsível. Para cenários complexos (retry, dead letter queues), considere Bull, uma abstração que roda sobre Redis.</p>
<h2>Session Store: Persistência de Sessões</h2>
<p>Armazenar sessões em Redis permite compartilhá-las entre múltiplos servidores Node.js sem estado compartilhado. O middleware <code>connect-redis</code> integra seamlessly com Express.</p>
<h3>Configuração de Sessions com Redis</h3>
<pre><code class="language-javascript">const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const app = express();
// Criar cliente Redis
const redisClient = createClient();
redisClient.connect();
// Configurar session store
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'sua_chave_secreta_aqui',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // true em produção com HTTPS
httpOnly: true,
maxAge: 1000 60 60 * 24 // 24 horas
}
}));
// Usar sessão
app.get('/login', (req, res) => {
req.session.userId = 123;
req.session.username = 'joao_silva';
res.send('Sessão criada');
});
app.get('/perfil', (req, res) => {
if (req.session.userId) {
res.json({
mensagem: Bem-vindo, ${req.session.username},
userId: req.session.userId
});
} else {
res.status(401).send('Não autenticado');
}
});
app.get('/logout', (req, res) => {
req.session.destroy((err) => {
res.send('Sessão destruída');
});
});
app.listen(3000);</code></pre>
<p>Sessions em Redis são recuperáveis automaticamente ao reiniciar o servidor, diferente de memory store padrão. Cada sessão expira conforme <code>maxAge</code>, mantendo o Redis limpo. Em produção, use Redis com persistência (RDB ou AOF) para durabilidade máxima.</p>
<h2>Conclusão</h2>
<p>Redis com Node.js oferece quatro capacidades complementares: <strong>cache</strong> reduz latência em leituras repetidas, <strong>pub/sub</strong> coordena comunicação em tempo real entre componentes, <strong>filas</strong> desacoplam processamento assincronamente, e <strong>session store</strong> centraliza estado de usuários em arquiteturas distribuídas. Domine esses padrões e sua aplicação escalará horizontalmente sem gargalos. Comece com cache em endpoints lidos frequentemente, evoluindo para arquiteturas baseadas em eventos conforme sua aplicação cresce.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://github.com/redis/node-redis" target="_blank" rel="noopener noreferrer">Documentação Oficial Redis Node.js Client</a></li>
<li><a href="https://github.com/tj/connect-redis" target="_blank" rel="noopener noreferrer">Express Session com Redis</a></li>
<li><a href="https://redis.io/commands/" target="_blank" rel="noopener noreferrer">Redis Commands Reference</a></li>
<li><a href="https://github.com/OptimalBits/bull" target="_blank" rel="noopener noreferrer">Bull: Queue Library para Node.js</a></li>
<li><a href="https://www.manning.com/books/redis-in-action" target="_blank" rel="noopener noreferrer">Redis in Action - Josiah L. Carlson</a></li>
</ul>