A health check that returns { ok: true } without testing database connectivity provides false assurance. When the database is unreachable, load balancers and deployment platforms interpret the 200 response as healthy and keep routing traffic to an application that cannot serve requests. ISO 25010 availability requires that the system's operational status is accurately observable; a health endpoint that does not reflect the actual state of its dependencies does not meet this requirement. Deployments that wait for a health check to pass before routing traffic also rely on this — a DB-unaware health check will green-light a deployment on a broken database.
Medium because a DB-unaware health endpoint causes deployment platforms to route traffic to broken instances and prevents automated recovery from detecting database failures.
Add a /api/health route that runs SELECT 1 against the database and returns 503 on failure. Wire this URL into your deployment platform's health check configuration.
// app/api/health/route.ts
import { prisma } from '@/lib/prisma'
export const dynamic = 'force-dynamic'
export async function GET() {
const checks: Record<string, 'ok' | 'fail'> = {}
try {
await prisma.$queryRaw`SELECT 1`
checks['database'] = 'ok'
} catch {
checks['database'] = 'fail'
}
const healthy = Object.values(checks).every(v => v === 'ok')
return Response.json(
{ status: healthy ? 'ok' : 'degraded', checks, ts: new Date().toISOString() },
{ status: healthy ? 200 : 503 }
)
}
Register the health check URL in your deployment platform (Railway, Render, ECS, Kubernetes readiness probe) so traffic is only routed to instances where the database is reachable.
ID: database-design-operations.monitoring-ops.health-check-endpoint
Severity: medium
What to look for: Count every health check endpoint found and search for health check endpoints in the API routes: /api/health, /api/healthz, /healthz, /health, /api/status, /status. Read the implementation of any found endpoint. Check whether it tests database connectivity — at minimum running SELECT 1 (Postgres/MySQL) or equivalent. A health check that only returns { status: 'ok' } without testing the database provides false assurance. Check the HTTP status code behavior: a healthy system should return 200, an unhealthy system (including unreachable database) should return 503 (Service Unavailable). Check whether the health endpoint is used in deployment platform health checks (Vercel, Railway, Render, ECS, Kubernetes readiness/liveness probes).
Pass criteria: A health check endpoint exists at minimum 1 of: /api/health, /healthz, /api/status, and tests database connectivity. The endpoint returns 200 when the database is reachable and 503 (or appropriate non-2xx code) when it's not. The endpoint is referenced in deployment platform configuration or documentation. Report the count of subsystem checks performed (database, cache, external services).
Fail criteria: No health check endpoint exists. Health check endpoint exists but returns 200 without testing database connectivity. Health check tests DB connectivity but doesn't distinguish 200/503 based on result.
Skip (N/A) when: Application has no server component — purely static site or client-only application with no API layer.
Detail on fail: Specify what is missing. Example: "No health check endpoint found in API routes." or "GET /api/health returns { ok: true } without testing database connectivity — unhealthy DB would not be detected.".
Remediation: Add a health check endpoint that tests database connectivity:
// app/api/health/route.ts (Next.js App Router)
import { prisma } from '@/lib/prisma'
export const dynamic = 'force-dynamic'
export async function GET() {
const checks: Record<string, 'ok' | 'fail'> = {}
// Test database connectivity
try {
await prisma.$queryRaw`SELECT 1`
checks['database'] = 'ok'
} catch {
checks['database'] = 'fail'
}
const healthy = Object.values(checks).every(v => v === 'ok')
return Response.json(
{
status: healthy ? 'ok' : 'degraded',
checks,
timestamp: new Date().toISOString(),
},
{ status: healthy ? 200 : 503 }
)
}
// For raw pg Pool
import { pool } from '@/lib/db'
try {
await pool.query('SELECT 1')
checks['database'] = 'ok'
} catch {
checks['database'] = 'fail'
}
Add the health check URL to your deployment platform's health check configuration (Vercel, Railway, Render, etc.) so deployments wait for the health check to pass before routing traffic.