React Starter Kit deploys as three Cloudflare Workers backed by a Neon PostgreSQL database. Infrastructure is managed with Terraform.
| Component | Target | Description |
|---|---|---|
| Web Worker | Cloudflare Workers | Edge router – receives all traffic, routes to app/api via service bindings |
| App Worker | Cloudflare Workers | Serves the React SPA and static assets |
| API Worker | Cloudflare Workers | Hono + tRPC server, authentication, database access |
| Database | Neon PostgreSQL | Managed Postgres with Hyperdrive connection pooling |
| Infrastructure | Terraform | Hyperdrive configurations, optional R2 storage |
See Architecture Overview for how these components connect.
brew install terraform or download)| Environment | Intended trigger | URL pattern | Purpose |
|---|---|---|---|
| Development | bun dev |
localhost:5173 |
Local development |
| Staging | Push to main |
staging.example.com |
Pre-production validation |
| Production | Manual dispatch | example.com |
Live environment |
Staging and production each have their own Wrangler configuration, Hyperdrive bindings, and Terraform workspace. Development is local and provisions no cloud resources. See CI/CD for deployment triggers. Automated deploys stay off until you set the DEPLOY_ENABLED repository variable, so a fresh clone runs CI only.
BETTER_AUTH_SECRET and RESEND_API_KEY, plus secrets for any optional integrations you enable, via Wrangler. See Cloudflare Workers for the full listapi, then app, then web. Service bindings resolve by name at deploy time, and web holds the public route, so it flips lastSteps 3 to 5 are what an automated release does; push to main or dispatch a production run and CI/CD handles them. To do it by hand:
# Preflight – nothing here changes production
bun run build
bun wrangler deploy --config apps/api/wrangler.jsonc --env="" --dry-run
bun wrangler deploy --config apps/app/wrangler.jsonc --env="" --dry-run
bun wrangler deploy --config apps/web/wrangler.jsonc --env="" --dry-run
# `--dry-run` never authenticates, so confirm the account separately
bun wrangler whoami --account <production-account-id>
Stop unless every command succeeded and whoami reports the account you meant. These are two blocks rather than one because an interactive shell does not stop on error – pasted together, a failed dry-run would scroll past and the migration would still run.
# Point of no return
bun db:migrate:production
bun api:deploy --env=""
bun app:deploy --env=""
bun web:deploy --env=""
The order matters for the same reason it does in CI: migrations run against workers that are still the old ones, so a schema change has to be additive until the new workers are live.