A health check endpoint is the small route a load balancer, Kubernetes, or a PaaS polls to decide two things: should it send traffic to this instance, and should it restart it? Without one, the platform can route real users to a process that is technically running but unable to serve requests. This guide gives you copy-paste health checks for Express, NestJS, and Next.js, plus the liveness-vs-readiness distinction and the right status codes. It's the framework-specific companion to The Node.js Production Readiness Checklist (2026).
Quick answer: expose a /health route that returns 200 OK when the app can serve traffic and 503 Service Unavailable when a critical dependency is down. In Express it's one route; in NestJS use @nestjs/terminus; in Next.js add app/health/route.ts (App Router) or pages/api/health.ts (Pages Router).
Liveness vs readiness — check the right thing
Orchestrators distinguish two probes, and conflating them causes restart loops:
- Liveness — "is the process alive?" If it fails, the container is restarted. Keep it cheap and dependency-free; a liveness probe that checks the database will restart a healthy app whenever the database blips.
- Readiness — "can it serve traffic right now?" If it fails, the process keeps running but is removed from rotation until it recovers. This is where you check dependencies.
A common setup is a trivial /health (or /livez) for liveness and a /ready (or /readyz) for readiness that verifies the database and any critical service.
Express
The minimal liveness endpoint is a single route. Register it before authentication middleware so the probe isn't blocked, and keep it fast:
// Liveness: cheap, no dependencies
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', uptime: process.uptime() });
});
A readiness endpoint verifies the things the app needs to actually work, and returns 503 when one is down so the load balancer stops routing to it:
// Readiness: check critical dependencies
app.get('/ready', async (req, res) => {
try {
await db.query('SELECT 1'); // is the database reachable?
res.status(200).json({ status: 'ready' });
} catch (err) {
res.status(503).json({ status: 'not-ready', reason: 'database' });
}
});
NestJS — @nestjs/terminus
NestJS has an official health-check module, @nestjs/terminus, with ready-made indicators for databases, HTTP services, disk, and memory. Install it alongside the platform HTTP package:
npm install @nestjs/terminus @nestjs/axios
Then create a health controller that runs a set of indicators. The endpoint returns an aggregated status, so /health reflects the real state of your dependencies and responds 503 automatically when an indicator fails:
import { Controller, Get } from '@nestjs/common';
import {
HealthCheck, HealthCheckService,
TypeOrmHealthIndicator, MemoryHealthIndicator,
} from '@nestjs/terminus';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
private memory: MemoryHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
() => this.memory.checkHeap('memory_heap', 300 * 1024 * 1024),
]);
}
}
Register TerminusModule in the module that provides HealthController, and Terminus handles the 200/503 response shape for you.
Next.js
A Next.js app can expose a health endpoint independent of its React pages. In the App Router, add a route handler:
// app/health/route.ts
import { NextResponse } from 'next/server';
export const dynamic = 'force-dynamic'; // never cache the probe
export async function GET() {
return NextResponse.json({ status: 'ok' }, { status: 200 });
}
In the older Pages Router, use an API route:
// pages/api/health.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ status: 'ok' });
}
If you deploy Next.js in a container, pair this with output: "standalone" in next.config.js for a smaller image — see the Docker section of the production readiness checklist.
Which status code?
Return 200 OK (any 2xx works) when healthy, and 503 Service Unavailable when a dependency the app needs is down. Avoid returning 200 unconditionally from a readiness probe — that defeats its purpose, because the platform keeps sending traffic to an instance that can't serve it. For the full map of status codes your API returns, see the HTTP Status Codes reference.
Does your project already have a health check?
The Deployment Readiness Checker detects your framework from package.json and reminds you to add the right health endpoint — along with the rest of your production checklist. Runs entirely in your browser.
Frequently Asked Questions
What HTTP status code should a health check return?
Return 200 OK when the app can serve traffic and 503 Service Unavailable when a critical dependency is down. Load balancers and orchestrators treat any 2xx as healthy and typically treat 503 (or a timeout) as unhealthy, so they stop routing traffic to that instance until it recovers.
What is the difference between liveness and readiness?
A liveness check answers "is the process alive?" — if it fails, the orchestrator restarts the container. A readiness check answers "can it serve traffic right now?" — if it fails, the orchestrator keeps the process running but stops sending it requests until dependencies (database, cache) are back. Liveness should be cheap and dependency-free; readiness may check dependencies.
How do I add a health check in Express?
Add a route: app.get('/health', (req, res) => res.status(200).json({ status: 'ok' })). Register it before authentication middleware so the probe is not blocked, keep it fast, and for a readiness variant check your database connection and return 503 if it is down.
How does NestJS handle health checks?
NestJS has an official module, @nestjs/terminus, that provides health indicators for databases, HTTP services, disk, and memory. You create a health controller that runs a set of indicators and returns an aggregated status, so /health reflects the real state of your dependencies.
Can a Next.js app have a health check endpoint?
Yes. In the App Router, add app/health/route.ts exporting a GET handler that returns a 200 response; in the Pages Router, add pages/api/health.ts. Both give you a lightweight endpoint your platform can poll, independent of your React pages.