|
| 1 | +# Foundry API Starter |
| 2 | + |
| 3 | +Production-ready Node.js API starter built with Hono, TypeScript, Drizzle ORM, Better Auth, Redis rate limiting, Prometheus metrics, Pino logging, Sentry tracing, and Resend-powered emails. This repo is meant to be a clean, extensible foundation for modern backend services. |
| 4 | + |
| 5 | +## Highlights |
| 6 | +- Hono + TypeScript with Zod-validated OpenAPI routes and auto-generated docs at `/docs` |
| 7 | +- Environment validation with sensible defaults and LAN-aware base URL detection |
| 8 | +- Pino logging (pretty in dev, JSON in prod) with request logging middleware |
| 9 | +- Redis-backed rate limiting and cache utilities |
| 10 | +- Drizzle ORM + Neon serverless Postgres driver with ready-to-run auth schema |
| 11 | +- Better Auth (Expo + bearer + admin + 2FA + orgs) wired for email flows via Resend |
| 12 | +- Prometheus metrics endpoint at `/api/v1/metrics` |
| 13 | +- Sentry instrumentation (opt-in via `SENTRY_DSN`) |
| 14 | +- CI pipeline for lint + build on push/PR |
| 15 | + |
| 16 | +## Getting Started |
| 17 | +1) **Install dependencies** |
| 18 | +```bash |
| 19 | +bun install |
| 20 | +``` |
| 21 | + |
| 22 | +2) **Copy env template and fill in secrets** |
| 23 | +```bash |
| 24 | +cp .env.example .env |
| 25 | +``` |
| 26 | +Required: `DATABASE_URL`, `BETTER_AUTH_SECRET`, `RESEND_API_KEY`, `EMAIL_FROM`, `REDIS_URL`, `FRONTEND_URL`, `BETTER_AUTH_URL`. |
| 27 | + |
| 28 | +3) **Run services** (local examples) |
| 29 | +```bash |
| 30 | +# Redis |
| 31 | +docker run -d --name redis -p 6379:6379 redis:7 |
| 32 | +``` |
| 33 | + |
| 34 | +4) **Start the API** |
| 35 | +```bash |
| 36 | +bun run dev |
| 37 | +``` |
| 38 | +The server will pick a LAN IP for `BASE_URL` in dev; visit `/api/v1/health` and `/docs`. |
| 39 | + |
| 40 | +## Scripts |
| 41 | +- `bun run dev` - watch mode via `tsx` |
| 42 | +- `bun run lint` - type-check only (`tsc --noEmit`) |
| 43 | +- `bun run build` - compile TypeScript and rewrite paths with `tsc-alias` |
| 44 | +- `bun start` - run compiled output (`dist`) |
| 45 | +- `bun run db:generate` - drizzle-kit generate |
| 46 | +- `bun run db:push` - drizzle-kit push |
| 47 | +- `bun run openapi:emit` - generate `docs/openapi.json` from the app |
| 48 | +- `bun run openapi:client` - generate typed client at `clients/openapi.ts` |
| 49 | +- `bun run contracts:build` - emit OpenAPI + client |
| 50 | +- `bun run contracts:test` - sanity-check the emitted OpenAPI JSON |
| 51 | +- `bun run test:auth-contract` - minimal auth/OpenAPI contract check |
| 52 | +- `bun run test:smoke` - in-process smoke checks for health/docs |
| 53 | +- `k6 run k6/smoke.js` - lightweight load/smoke from the outside (set `BASE_URL`) |
| 54 | + |
| 55 | +## Environment |
| 56 | +See `.env.example` for defaults. Key variables: |
| 57 | +- `APP_NAME`, `APP_VERSION`, `APP_SCHEME`, `APP_WEB_URL` |
| 58 | +- `BASE_URL`, `PORT`, `LOG_LEVEL` |
| 59 | +- `DATABASE_URL`, `FRONTEND_URL` |
| 60 | +- `BETTER_AUTH_URL`, `BETTER_AUTH_SECRET` |
| 61 | +- `RESEND_API_KEY`, `EMAIL_FROM` |
| 62 | +- `SENTRY_DSN`, `SENTRY_ENV`, `SENTRY_RELEASE` |
| 63 | +- `REDIS_URL` |
| 64 | +- `CORS_EXTRA_ORIGINS` |
| 65 | +- `RATE_LIMIT_GLOBAL_MAX`, `RATE_LIMIT_AUTH_MAX` |
| 66 | +- `STORAGE_ENDPOINT`, `STORAGE_ACCESS_KEY`, `STORAGE_SECRET_KEY`, `STORAGE_BUCKET`, `STORAGE_USE_SSL` |
| 67 | +- `INNGEST_EVENT_KEY` |
| 68 | + |
| 69 | +## API Surface |
| 70 | +- `/api/v1/health` - health check |
| 71 | +- `/api/v1/metrics` - Prometheus metrics |
| 72 | +- `/api/v1/auth/*` - Better Auth handler (REST + OpenAPI) |
| 73 | +- `/docs` - OpenAPI JSON + docs UI |
| 74 | +- `/docs/ui` - Scalar-powered API reference UI (OpenAPI from `/docs`) |
| 75 | + |
| 76 | +## Architecture Notes |
| 77 | +- **App lifecycle**: graceful signal handling + unhandled error capture to Sentry (prod) |
| 78 | +- **Middleware**: Sentry context, Prometheus registry, Pino request logger (with IP/UA + request IDs), Redis rate limiter (global + stricter auth limiter), CORS + security headers, global error handler, 404 handler |
| 79 | +- **Auth**: Better Auth with Drizzle adapter (Postgres), Expo support, bearer signing, admin, orgs, 2FA, email verification/reset flows via Resend |
| 80 | +- **Email**: React email templates (invite, OTP, verify, reset, welcome) branded via `APP_NAME` |
| 81 | +- **Storage**: MinIO/S3 helper for presigned URLs and uploads |
| 82 | +- **Async**: Inngest client stub for event-based jobs (set `INNGEST_EVENT_KEY` to enable sending) |
| 83 | +- **Contracts**: OpenAPI doc emit + typed client generation via `openapi-typescript` |
| 84 | +- **Domain scaffold**: Example `task` table tied to `user` (`src/db/schema/tasks.ts`) and Inngest task handlers (`src/jobs/tasks.ts`) as placeholders to extend |
| 85 | + |
| 86 | +## Observability |
| 87 | +- **Sentry**: set `SENTRY_DSN` (plus `SENTRY_ENV` and `SENTRY_RELEASE`) to capture errors; unhandled errors and 500s flush on shutdown. |
| 88 | +- **Metrics**: `/api/v1/metrics` Prometheus endpoint; sample scrape config in `docs/observability/prometheus.yml`. |
| 89 | +- **Logs**: Pino JSON in production (stdout). Ship to Loki/ELK/Datadog via an agent; sample Promtail file shipping `/var/log/foundry-api.log` in `docs/observability/promtail-config.yaml`. |
| 90 | +- **Dashboards**: Import `docs/observability/grafana-dashboard.json` and point datasource UID `PROM_DS` at your Prometheus instance. |
| 91 | +- **Crash test**: `/api/v1/crash` throws to validate alerting and error reporting. |
| 92 | + |
| 93 | +## Local Dev Services |
| 94 | +- `docker-compose.yml` spins up Postgres, Redis, and MinIO (S3-compatible). |
| 95 | +- Defaults assume `STORAGE_ENDPOINT=http://localhost:9002`, Postgres on 5432, Redis on 6379. |
| 96 | +- Make sure the API port (9000) does not conflict with MinIO (mapped to 9002). |
| 97 | + |
| 98 | +## Developer Experience |
| 99 | +- `Makefile` shortcuts: `make dev`, `make lint`, `make format`, `make build`, `make compose-up/down`. |
| 100 | +- Pre-commit config (`.pre-commit-config.yaml`) ready for formatting/lint hooks if you use the `pre-commit` tool. |
| 101 | + |
| 102 | +## CI/CD |
| 103 | +GitHub Actions workflow runs on push/PR: |
| 104 | +- Install dependencies with Bun |
| 105 | +- `bun run lint` (type-check) |
| 106 | +- `bun run build` |
| 107 | +Extend with deploy, migrations, and smoke tests as you wire up your platform. |
| 108 | + |
| 109 | +## Production Checklist |
| 110 | +- Provide real secrets for database, Redis, Resend, Better Auth, and Sentry |
| 111 | +- Set `BASE_URL`, `FRONTEND_URL`, and `BETTER_AUTH_URL` to public hosts |
| 112 | +- Run behind TLS and put a reverse proxy (Caddy/Nginx) in front |
| 113 | +- Configure process manager (systemd/PM2) and health checks on `/api/v1/health` |
| 114 | +- Enable log shipping (Pino JSON) and metrics scraping on `/api/v1/metrics` |
| 115 | +- Generate and ship OpenAPI (`bun run contracts:build`) and distribute `clients/openapi.ts` to consumers |
| 116 | +- Validate crash reporting with `/api/v1/crash`; verify Prometheus scrape and log shipping |
| 117 | +- Tighten CORS origins per environment and rotate credentials regularly |
| 118 | +- Run external smoke/load: `k6 run k6/smoke.js -e BASE_URL=https://api.yourdomain.com -e VUS=10 -e DURATION=2m` |
| 119 | +- Build domain: extend Drizzle schemas/migrations (e.g., `src/db/schema/tasks.ts`), add services/routes, and wire Inngest handlers (`src/jobs/tasks.ts`) |
| 120 | + |
| 121 | +## Reverse Proxy |
| 122 | +- See `deploy/nginx.conf` for an example Nginx config (HTTP→HTTPS redirect, gzip, proxy headers). Replace certificate paths and back-end host/port as needed. |
| 123 | + |
| 124 | +## Next Steps (for future build-out) |
| 125 | +- Add infra for file storage (e.g., MinIO/S3), background jobs (Inngest), and queueing |
| 126 | +- Expand database schemas for domain models (finance/debts/baby steps stubs are placeholders) |
| 127 | +- Harden auth flows (rate limiting per route, IP/device fingerprinting, email domain rules) |
| 128 | +- Add security headers like CSP and stricter CORS policies per environment |
| 129 | +- Add end-to-end smoke tests and load tests |
0 commit comments