Conceptos Básicos de Resiliencia
8 ejemplos para empezar con la Resiliencia para backends de Node.js: 6 básicos y 2 intermedios.
Busca en todas las páginas de la documentación
8 ejemplos para empezar con la Resiliencia para backends de Node.js: 6 básicos y 2 intermedios.
fetch nativo y AbortSignal.timeout.fetch SalienteLas llamadas descendentes colgadas agotan tu pool de conexiones.
const response = await fetch("https://api.partner.com/data", {
signal: AbortSignal.timeout(5_000),
});AbortSignal.timeout disponible en Node 18+ - sin setTimeout manual.TimeoutError a 504 o a una política de reintentos ascendente.Relacionado: Tiempos de espera en todas partes - matriz completa de tiempos de espera
No sirvas tráfico con un entorno inválido.
import { z } from "zod";
const Env = z.object({
DATABASE_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
});
export const env = Env.parse(process.env); // lanza un error antes de listen()Relacionado: Conceptos Básicos de Configuración - análisis de entorno con Zod
Dile a Kubernetes si debe reiniciar o dejar de enviar tráfico.
app.get("/health", (_req, res) => res.json({ ok: true }));
app.get("/ready", async (_req, res) => {
try {
await db.query("SELECT 1");
res.json({ ready: true });
} catch {
res.status(503).json({ ready: false });
}
});Relacionado: Conceptos Básicos de Observabilidad - patrones de sonda
Drena el tráfico HTTP en curso antes de salir en el despliegue.
const server = app.listen(3000);
process.on("SIGTERM", () => {
server.close(() => {
logger.info({ event: "shutdown_complete" });
process.exit(0);
});
setTimeout(() => process.exit(1), 30_000).unref();
});server.close deja de aceptar nuevas conexiones; finaliza las activas.terminationGracePeriodSeconds de k8s.Relacionado: Degradación Elegante - modos de desactivación de funciones
Un POST sin claves de idempotencia puede cobrar dos veces.
async function safeGetWithRetry(url: string, attempts = 3): Promise<Response> {
for (let i = 0; i < attempts; i++) {
try {
const res = await fetch(url, { signal: AbortSignal.timeout(3_000) });
if (res.status < 500) return res;
} catch (err) {
if (i === attempts - 1) throw err;
}
await delay(100 * 2 ** i);
}
throw new Error("unreachable");
}Relacionado: Reintentos con Backoff - jitter e idempotencia
Los usuarios y los balanceadores de carga necesitan una respuesta dentro del SLA.
app.use((req, res, next) => {
const timer = setTimeout(() => {
if (!res.headersSent) {
res.status(504).json({ error: "Gateway Timeout" });
}
}, 30_000);
res.on("finish", () => clearTimeout(timer));
next();
});requestId para correlación.Relacionado: Tiempos de espera en todas partes - tiempos de espera del servidor
Deja de llamar a una dependencia que falla para permitirle recuperarse.
let failures = 0;
const THRESHOLD = 5;
let openUntil = 0;
async function callBilling() {
if (Date.now() < openUntil) throw new Error("Circuit open");
try {
const result = await billingClient.charge();
failures = 0;
return result;
} catch (err) {
failures++;
if (failures >= THRESHOLD) openUntil = Date.now() + 30_000;
throw err;
}
}opossum.Relacionado: Disyuntores - patrones de opossum
Un proveedor lento no puede consumir todos los sockets.
import { Semaphore } from "async-mutex"; // o pool personalizado
const vendorSem = new Semaphore(10);
async function callVendor() {
const [, release] = await vendorSem.acquire();
try {
return await fetch(VENDOR_URL, { signal: AbortSignal.timeout(2_000) });
} finally {
release();
}
}Relacionado: Disyuntores - bulkhead con opossum
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.
Revisado por Chris St. John·Última actualización: 16 jul 2026