Skip to content

[Feat] Add rate-limited /health endpoint reporting DB and Redis connectivity with app version#23

Draft
roomote-roomote-v1[bot] wants to merge 1 commit into
developfrom
feature/api-health-endpoint-rate-limit-31qvhb7uek4gy
Draft

[Feat] Add rate-limited /health endpoint reporting DB and Redis connectivity with app version#23
roomote-roomote-v1[bot] wants to merge 1 commit into
developfrom
feature/api-health-endpoint-rate-limit-31qvhb7uek4gy

Conversation

@roomote-roomote-v1

Copy link
Copy Markdown

Opened on behalf of Daniel Riccio. View the task or mention @openmote for follow-up asks.

What changed

The API app now serves GET /health as an alias of the existing DB + Redis readiness handler (the same one behind / and /health/api), with three behavior additions:

  • Both dependency checks always run. The old DB-failure short-circuit is gone: the PostgreSQL SELECT 1 and Redis PING checks now run in parallel, so a 503 response always reflects the state of both dependencies (and worst-case latency is bounded by the slower check instead of the sum).
  • Authenticated payloads report the running app version. A new version field resolves RELEASE_VERSIONVERCEL_GIT_COMMIT_SHAGITHUB_SHAdevelopment (release-first variant of the API's Sentry release chain). Unauthenticated callers still receive only the redacted { server, ok, timestamp } payload.
  • /health is rate limited per client IP. A new in-memory fixed-window limiter (healthRateLimitMiddleware) allows 60 requests/minute by default via the new API_HEALTH_RATE_LIMIT_PER_MINUTE env var (0 disables) and returns 429 with a Retry-After header when exceeded. It is keyed by the first x-forwarded-for hop with a conninfo fallback, caps tracked clients at 10k with expired-window sweeping plus earliest-tracked eviction, and applies only to the bare /health path — /, /health/api, and /health/liveness stay unlimited for Better Stack monitoring and deployment healthchecks.

Supporting changes: /health joined the request-metrics exclusion list, the env schema and .env.production.example document the new variable, and the .agent-guidance monitoring and API-app docs were updated to match.

Why this change was made

Operators asked for a conventional /health path that reports Redis and database connectivity, with basic abuse dampening and version visibility for diagnostics. Reusing the existing handler keeps one owning health surface per apps/api guidance instead of adding an overlapping endpoint. The limiter is deliberately not Redis-backed: a health check that verifies Redis must not depend on Redis, and per-replica budgets are acceptable at this scope.

Impact

  • GET /health responds 200/503 with the standard redacted public payload; bearer-token callers get the full diagnostics plus version.
  • Behavior change for the shared handler: when the database is down, /, /health/api, and /health now also report Redis state instead of short-circuiting (same status codes; the response error still prefers the DB failure).
  • No deployment config changes: platform healthchecks use the untouched, dependency-free /health/liveness.
  • The rate limit is best-effort by design (per process, spoofable XFF, unlimited sibling mounts); it dampens hammering rather than acting as a security control.

Covered by new/updated Vitest suites: handler tests (parallel checks, redis-only failure, version redaction and fallback), limiter tests (budget, Retry-After, window rollover, per-IP isolation, multi-hop XFF, disable switch, cap eviction, unlimited sibling routes), and a live-HTTP integration test exercising /health and the 429 path end to end. pnpm lint, pnpm check-types, and pnpm knip pass.

@roomote-roomote-v1

roomote-roomote-v1 Bot commented Jul 9, 2026

Copy link
Copy Markdown
Author

No code issues found. See task

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