Timeouts Everywhere
Set timeouts on server requests, outbound HTTP, database queries, and queues - unbounded waits are the most common cause of cascading Node.js API outages.
Search across all documentation pages
Set timeouts on server requests, outbound HTTP, database queries, and queues - unbounded waits are the most common cause of cascading Node.js API outages.
Quick-reference recipe card - copy-paste ready.
import { createServer } from "node:http";
const server = createServer(app);
server.requestTimeout = 30_000;
server.headersTimeout = 35_000;
server.keepAliveTimeout = 65_000;
const data = await fetch(url, { signal: AbortSignal.timeout(5_000) });When to reach for this:
fetch or axios call without explicit timeout.import express from "express";
import { createServer } from "node:http";
import pg from "pg";
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
connectionTimeoutMillis: 3_000,
query_timeout: 5_000, // node-pg option wrapping statement timeout
});
const app = express();
app.get("/reports/:id", async (req, res) => {
const controller = AbortController ? new AbortController() : null;
const deadline = setTimeout(() => controller?.abort(), 8_000);
try {
const report = await pool.query(
"SELECT * FROM reports WHERE id = $1",
[req.params.id]
);
const enrichment = await fetch(
`https://analytics.internal/enrich/${req.params.id}`,
{ signal: controller?.signal ?? AbortSignal.timeout(8_000) }
);
if (!enrichment.ok) {
return res.status(502).json({ error: "Analytics unavailable" });
}
res.json({ report: report.rows[0], analytics: await enrichment.json() });
} catch (err) {
if ((err as Error).name === "AbortError") {
return res.status(504).json({ error: "Upstream timeout" });
}
throw err;
} finally {
clearTimeout(deadline);
}
});
const server = createServer(app);
server.requestTimeout = 30_000;
server.listen(3000);What this demonstrates:
AbortError mapped to 504 Gateway Timeout for clients.createServer timeouts protect against slowloris and hung sockets.requestTimeout - max time for full request on socket (Node HTTP server).AbortSignal.timeout - aborts fetch after N ms in Node 18+.statement_timeout (Postgres) - DB kills long queries regardless of app state.| Layer | Typical value | Failure response |
|---|---|---|
| Outbound HTTP | 2-5s user path | 502/504 |
| DB query | 3-10s | 500 + log |
| Handler budget | 10-15s | 504 |
Server requestTimeout | 30s | connection reset |
| LB idle | 60s | 502 |
SET statement_timeout = '5s';
-- or per role:
ALTER ROLE app_user SET statement_timeout = '5s';await pool.query("SET statement_timeout = 5000");import axios from "axios";
const client = axios.create({ timeout: 5_000 });timeout covers connection + response; use signal: AbortSignal.timeout() for finer control.AbortSignal.timeout.headersTimeout - must exceed keepAliveTimeout slightly on Node HTTP server.finally. Fix: avoid leaked handles.| Alternative | Use When | Don't Use When |
|---|---|---|
| AbortSignal.timeout | Native fetch in Node 18+ | Legacy Node 16 |
| undici Agent timeouts | Shared client defaults | One-off fetch |
| axios timeout | Axios-based codebase | Native fetch only |
| Deadline context (gRPC) | gRPC services | REST JSON APIs |
2-5s per outbound hop; 10-15s total handler budget; 30s server max. Tune per SLO.
DB query timeout should be less than handler budget - fail query before HTTP deadline.
No default - infinite wait. Always pass signal: AbortSignal.timeout(ms).
Use serverFactory with createServer options or @fastify/request-timeout plugin patterns.
Set timeout on job options - separate from HTTP timeouts but same philosophy.
504 gateway timeout - upstream too slow. 503 service unavailable - dependency down or circuit open.
Mobile apps should timeout too - but server must not wait for client patience.
Timeouts trigger failures that increment breaker counters. See Circuit Breakers.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026