feat(prod): /api/health + boot-time env validation#22
Open
salishforge wants to merge 1 commit into
Open
Conversation
First slice of production-readiness work. Two thin additions, no
behavior change to existing routes:
src/lib/env.ts — pure validator
Pass in a Record<string, string|undefined>, get back a structured
result with errors and warnings. Two severity bands:
- Required at boot (DATABASE_URL, NEXT_PUBLIC_APP_URL,
AUTH_MODE-explicit, Supabase keys when AUTH_MODE=supabase). In
production, missing → throw. Catches the audit-found case where
NEXT_PUBLIC_APP_URL silently falls back to localhost:3000 and
Stripe success/cancel redirects route real prod traffic to dev.
- Feature-critical (Stripe, Resend, Novu, eBay, PriceCharting,
Anthropic, cron). Missing → warning only; the feature's own
client decides whether to throw or no-op when actually used.
Also rejects production NEXT_PUBLIC_APP_URL values containing
localhost / 127.0.0.1 or missing http(s):// — covers copy-pasted
dev values that pass non-empty presence checks.
src/instrumentation.ts — Next 16 instrumentation hook
Calls assertEnv() once at server boot. Node-runtime-only (the
validator's surface is server-side). In production, an aggregated
error refuses to start the server; in dev, warnings log but boot
continues.
src/app/api/health/route.ts — /api/health
GET-only, no auth (LBs and uptime probes need to reach it):
- SELECT 1 against the DB; 200/ok on success, 503/down on failure.
- Re-runs validateEnv() each call so post-restart env rotations
surface without a redeploy; never echoes secret values.
- Reports uptimeSeconds + ISO timestamp.
.env.example — three audit-found gaps
- NEXT_PUBLIC_APP_URL: documented as production-critical.
- WONDERS_DECK_PLATFORM_API_URL: used by platform/client.ts.
- WONDERSTRADINGPOST_ANON_KEY: public anon key surfaced for
upstream-rotation visibility.
Tests
- tests/env/env.test.ts (18 cases): production posture for each
required var, dev posture (warnings only), AUTH_MODE matrix,
feature-warning roll-ups, empty/whitespace-as-missing.
- tests/health/health.test.ts (3 cases): 200 happy path, 503 on
DB ping failure, env-warnings block shape.
Verification
- npx tsc --noEmit clean
- npm run lint clean on touched files (0 errors, 0 warnings)
- npx vitest run tests/env tests/health tests/attribution tests/packs
→ 44/44 (21 new + 23 existing)
- DATABASE_URL=… npm run build → exit 0, 39 routes compiled
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First slice of production-readiness work toward the beta launch. Two thin additions, no behaviour change to existing routes:
src/lib/env.ts— pure environment validator. Two severity bands:NEXT_PUBLIC_APP_URLsilently falls back tohttp://localhost:3000and Stripe success/cancel redirects route real prod traffic to dev.Also rejects production
NEXT_PUBLIC_APP_URLvalues containinglocalhost/127.0.0.1or missinghttp(s)://— copy-pasted dev values that pass non-empty presence checks but break in prod.src/instrumentation.ts— Next 16 instrumentation hook. CallsassertEnv()once at server boot. Node-runtime-only. In production an aggregated error refuses to start the server; in dev warnings log but boot continues.src/app/api/health/route.ts—GET /api/health. No auth (LBs and uptime probes need to reach it).SELECT 1against the DB → 200/okon success, 503/downon failure. Re-runsvalidateEnv()each call so post-restart env rotations surface without a redeploy. Never echoes secret values — only category warnings like "stripe: not configured"..env.example— three audit-found gaps documented:NEXT_PUBLIC_APP_URL— production-criticalWONDERS_DECK_PLATFORM_API_URL— used byplatform/client.tsfor engine-metric syncWONDERSTRADINGPOST_ANON_KEY— public anon key surfaced for upstream-rotation visibilityTests
tests/env/env.test.ts— 18 cases: production posture for each required var, dev posture (warnings only), AUTH_MODE matrix, feature-warning roll-ups, empty/whitespace-as-missing semantics.tests/health/health.test.ts— 3 cases: 200 happy path, 503 on DB ping failure, env-warnings block shape.Verification
npx tsc --noEmitcleannpm run lint— 0 errors, 0 warnings on touched filesnpx vitest run→ 44/44 across env, health, attribution, packs (21 new + 23 pre-existing)DATABASE_URL=… npm run build→ exit 0, 39 routes compiledContext
Per the production-readiness assessment in this session, this is one chunk of P0 work. Other P0 items (rebase + land #21, security headers, admin-bootstrap script, Dockerfile, prod migration runner) are still ahead.
Generated by Claude Code