Skip to content

feat: add /api/health endpoint for healthy.sh monitoring#1

Draft
Manan-Santoki wants to merge 1 commit into
mainfrom
healthysh/health-endpoint
Draft

feat: add /api/health endpoint for healthy.sh monitoring#1
Manan-Santoki wants to merge 1 commit into
mainfrom
healthysh/health-endpoint

Conversation

@Manan-Santoki

Copy link
Copy Markdown
Owner

What

Adds GET /api/health (src/app/api/health/route.ts) so the healthy.sh fleet monitor can probe Binderly. The route runs on the Node.js runtime with dynamic = "force-dynamic" (never cached) and checks Postgres connectivity with a SELECT 1 through the repo's existing getSql helper (now exported from src/lib/db.ts). The DB module is lazily imported inside the handler, so a missing or broken DATABASE_URL can never crash module load or the build.

Response contract

Condition HTTP Body
Postgres reachable 200 { "status": "ok", "service": "binderly", "checks": { "postgres": { "ok": true, "latency_ms": 12 } } }
Postgres configured but unreachable 503 { "status": "degraded", "service": "binderly", "checks": { "postgres": { "ok": false, "error": "<msg>" } } }
DATABASE_URL unset 200 { "status": "degraded", "service": "binderly", "checks": { "postgres": { "ok": false, "error": "unconfigured" } } }

DATABASE_URL unset is a soft degradation (HTTP 200): the editor and PDF export work fine without Postgres — only sharing needs it.

Optional token gating

If the env var HEALTHSH_HEALTH_TOKEN is set, requests must present it via Authorization: Bearer <token> or ?token=<token>. Requests without a valid token get the same HTTP status code but a body of only { "status": "..." } — check details (error messages, latency) are omitted. If the env var is unset, the endpoint is fully open (it exposes no secrets either way, but error strings can hint at infra details).

How healthy.sh consumes this

The monitor registers a json_health check against https://<host>/api/health asserting $.status == "ok", with the HTTP status code as a secondary signal (200 = up/soft-degraded, 503 = postgres down). latency_ms is recorded as a metric.

Testing

pnpm dev
curl -s localhost:3000/api/health | jq .            # full body
curl -s -o /dev/null -w '%{http_code}\n' localhost:3000/api/health
# with token gating:
HEALTHSH_HEALTH_TOKEN=secret pnpm dev
curl -s localhost:3000/api/health | jq .            # -> { "status": "..." } only
curl -s -H 'Authorization: Bearer secret' localhost:3000/api/health | jq .
curl -s 'localhost:3000/api/health?token=secret' | jq .

Verified locally with pnpm tsc --noEmit (clean).

🤖 Generated with Claude Code

Adds GET /api/health for uptime monitoring. Reports overall status plus
a Postgres connectivity check (SELECT 1 via the existing getSql helper,
lazily imported). Returns 200 {status:"ok"} when healthy, 503
{status:"degraded"} when Postgres is configured but unreachable, and a
soft 200 {status:"degraded"} when DATABASE_URL is unset since the
editor and PDF export work without it. Optional HEALTHSH_HEALTH_TOKEN
env var gates check details (Bearer header or ?token= query param);
unauthenticated callers only see {status}.

Also exports getSql from src/lib/db.ts so the route can reuse the
shared client.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant