API Gateway Patterns: Autenticação, Rate Limiting e Caching com Node.js
Um reverse proxy que só repassa requests é um nginx com extra steps. Um API Gateway de verdade resolve três problemas antes que o request chegue ao serviço de destino: quem é você (autenticação), com que frequência você pode bater aqui (rate limiting) e se eu preciso processar isso de novo (caching). O resto (transformação de payload, circuit breaking, observabilidade) é camada extra que só faz sentido depois que esses três estão sólidos.
Este post implementa cada um desses patterns com Express, Redis e TypeScript. Código completo, rodável, sem abstrações fictícias.
A anatomia de um gateway: ordem dos middlewares importa
A sequência em que você encadeia os middlewares define o comportamento do gateway. Inverter a ordem entre rate limiting e autenticação, por exemplo, muda completamente o perfil de segurança.
// src/gateway.ts
import express from 'express';
import { authMiddleware } from './middlewares/auth';
import { rateLimitMiddleware } from './middlewares/rateLimit';
import { cacheMiddleware } from './middlewares/cache';
import { proxyMiddleware } from './middlewares/proxy';
const app = express();
// 1. Rate limiting ANTES de autenticação: protege contra brute force no próprio endpoint de login
app.use(rateLimitMiddleware);
// 2. Autenticação: rejeita requests sem token válido antes de gastar I/O com cache ou proxy
app.use(authMiddleware);
// 3. Cache: evita bater no upstream se a resposta ainda é válida
app.use(cacheMiddleware);
// 4. Proxy: repassa para o serviço de destino só o que passou por tudo acima
app.use(proxyMiddleware);
app.listen(3000, () => {
console.log('Gateway rodando na porta 3000');
});
Se você colocar autenticação antes de rate limiting, um atacante consegue disparar milhares de requests com tokens inválidos e forçar o gateway a verificar JWT (operação CPU-bound com verificação de assinatura) antes de qualquer throttling. O rate limiter na frente corta isso na raiz.
Autenticação centralizada com JWT e JWKS
Validar JWT no gateway significa que nenhum serviço downstream precisa conhecer chaves de assinatura. O gateway valida, extrai claims e repassa como headers confiáveis.
// src/middlewares/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt, { JwtHeader, SigningKeyCallback } from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: process.env.JWKS_URI || 'https://auth.exemplo.com/.well-known/jwks.json',
// Cache de chaves por 10 minutos: evita bater no JWKS endpoint a cada request
cache: true,
cacheMaxAge: 600_000,
rateLimit: true,
jwksRequestsPerMinute: 10,
});
function getKey(header: JwtHeader, callback: SigningKeyCallback): void {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
const signingKey = key?.getPublicKey();
callback(null, signingKey);
});
}
const PUBLIC_PATHS = ['/health', '/api/v1/auth/login', '/api/v1/auth/register'];
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
if (PUBLIC_PATHS.includes(req.path)) {
return next();
}
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
res.status(401).json({ error: 'Token ausente' });
return;
}
const token = authHeader.slice(7);
jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) {
const status = err.name === 'TokenExpiredError' ? 401 : 403;
res.status(status).json({ error: err.message });
return;
}
// Headers X-User-* são confiáveis porque só o gateway os injeta
// Serviços downstream NUNCA devem aceitar esses headers vindos do cliente
const payload = decoded as { sub: string; role: string; tenant_id: string };
req.headers['x-user-id'] = payload.sub;
req.headers['x-user-role'] = payload.role;
req.headers['x-tenant-id'] = payload.tenant_id;
next();
});
}
O uso de JWKS (JSON Web Key Set) em vez de u
Leia o artigo completo em https://www.vivodecodigo.com.br/backend/api-gateway-patterns-autenticacao-rate-limiting-caching-nodejs