Node.js cookbook · Beginner
Add health and readiness endpoints to a Node.js service
Let Kubernetes restart stuck processes and route traffic only to instances that can serve it.
Approach
/healthz checks only that the event loop responds (optionally that event-loop lag is sane). /readyz pings critical dependencies with timeouts and returns 503 when not ready or shutting down. Keep them unauthenticated, cheap, and out of request logs.
Solution
import { monitorEventLoopDelay } from "node:perf_hooks";
const loopDelay = monitorEventLoopDelay({ resolution: 20 });
loopDelay.enable();
const withTimeout = <T>(p: Promise<T>, ms: number) =>
Promise.race([p, new Promise<never>((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))]);
app.get("/healthz", (_req, res) => {
const p99ms = loopDelay.percentile(99) / 1e6;
loopDelay.reset();
res.status(p99ms < 1000 ? 200 : 503).json({ status: "ok", eventLoopP99Ms: Math.round(p99ms) });
});
app.get("/readyz", async (_req, res) => {
if (shuttingDown) return res.status(503).json({ status: "shutting_down" });
const checks = await Promise.allSettled([
withTimeout(pool.query("SELECT 1"), 1000),
withTimeout(redis.ping(), 1000),
]);
const [db, cache] = checks.map((c) => (c.status === "fulfilled" ? "ok" : "fail"));
const ready = db === "ok" && cache === "ok";
res.status(ready ? 200 : 503).json({ status: ready ? "ready" : "not_ready", checks: { db, cache } });
});
Pitfalls
- Liveness depending on the database.
- Probes without timeouts.
- Heavy work in probes running every few seconds.
Variations
@godaddy/terminusbundles health checks with graceful shutdown.
Further reading: Kubernetes: Probes
More Node.js recipes
- Add JWT authentication to an Express API
- Cache expensive lookups in Redis from Node.js
- Call external APIs with timeouts and retries in Node.js
- Centralize error handling in Express 5
- Hash passwords and implement login safely in Node.js
- Implement cursor pagination with PostgreSQL in Node.js
- Offload CPU-heavy work with worker threads
- Process background jobs with BullMQ