A full-stack TypeScript monorepo for building web applications on Cloudflare Workers. It combines a SvelteKit frontend with a Hono API, Effect services, PostgreSQL, and end-to-end type-safe API calls.
The starter includes a complete email/password authentication flow and a protected example endpoint, so you can begin with application code instead of wiring together infrastructure.
- SvelteKit and Svelte 5
- Hono with typed RPC clients
- Effect for backend services and error handling
- Better Auth for authentication
- Drizzle ORM and PostgreSQL
- Formisch and Valibot for typed forms and validation
- TanStack Query and neverthrow
- Tailwind CSS and shadcn-svelte
- Cloudflare Workers and Hyperdrive
- pnpm workspaces, Vitest, Oxlint, and Oxfmt
- Registration, login, logout, and session-protected pages
- SSR through SvelteKit's Cloudflare adapter, including server-side auth guards
- Formisch forms backed by shared Valibot schemas
- Better Auth tables and committed Drizzle migrations
- Shared Valibot DTOs for frontend and backend validation
- Generated Hono types consumed directly by the web app
- Generated Cloudflare binding types exposed through SvelteKit's
App.Platform - Service bindings for server-side Worker-to-Worker API calls
- A typed
callApihelper that exposes responses asResultvalues - A protected SHA-256 example endpoint at
POST /api/hash - Cloudflare Workers integration tests for the API
.
├── apps/
│ ├── api/ # Hono Worker, Effect services, auth, and database
│ └── web/ # SvelteKit application
├── packages/
│ └── dto/ # Shared validation schemas and inferred types
├── package.json # Workspace commands
└── pnpm-workspace.yaml
- Node.js
- pnpm 10 (the repository pins the exact version through
packageManager) - PostgreSQL available at a local connection URL
- A Cloudflare account and Wrangler authentication only when deploying
-
Install dependencies:
pnpm install
-
Create the API environment file:
cp apps/api/env.example apps/api/.env
Set
BETTER_AUTH_SECRETto a strong random value. For example:openssl rand -base64 32
The default API configuration expects PostgreSQL at
postgresql://postgres:root@localhost:5432/postgres. ChangeCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVEif your local database uses a different URL. -
Create the web environment file:
cp apps/web/env.example apps/web/.env
PROXY_API_ORIGINdefaults to the local Wrangler API athttp://localhost:8787, allowing browser requests to use the same/apipaths in development and production. -
Apply the database migrations:
pnpm api:db:migrate
-
Start the development environment:
pnpm dev
The web app is available at http://localhost:5173, and the API health endpoint is available at http://localhost:8787/api/health.
The root development command starts the API, Hono type generation, Drizzle Studio, and the SvelteKit development server together.
| Command | Description |
|---|---|
pnpm dev |
Start the complete local development environment |
pnpm web:dev |
Start only the SvelteKit app |
pnpm api:dev |
Start only the Hono Worker |
pnpm web:check |
Run Svelte and TypeScript checks |
pnpm api:test |
Run API tests in the Workers runtime |
pnpm lint |
Lint the workspace with Oxlint |
pnpm lint:fix |
Apply safe lint fixes |
pnpm format |
Format the workspace with Oxfmt |
pnpm format:check |
Check formatting without changing files |
pnpm api:db:generate |
Regenerate auth schema and create a migration |
pnpm api:db:migrate |
Apply pending database migrations |
pnpm api:db:studio |
Open Drizzle Studio |
pnpm api:typegen |
Regenerate Cloudflare and Hono types |
pnpm web:build |
Build the web Worker |
pnpm web:typegen:cf |
Regenerate web Worker binding types |
The API exports its Hono application type from apps/api/src/app.ts. The api package generates declarations into apps/api/dist-types, and the web app imports AppType through api/rpc.
Shared request schemas live in packages/dto. A route can validate one of those schemas with Hono's standard validator, while the frontend uses the same schema for form validation. The resulting RPC client infers request bodies, success payloads, error payloads, and status codes without maintaining a separate API specification.
When adding or changing API routes, regenerate declarations with:
pnpm api:typegen:honoThe root pnpm dev command watches these declarations automatically.
Login, registration, and the protected hash example use Formisch for form state, submission, and field-level errors. Their schemas come from the shared packages/dto workspace package, where Valibot defines both runtime validation and the corresponding TypeScript types.
The API consumes those same schemas through Hono's standard validator. This keeps browser validation and API validation aligned without duplicating request models.
The web application supports SvelteKit SSR and targets Cloudflare Workers through @sveltejs/adapter-cloudflare.
API access works in both rendering environments:
- In the browser, the typed Hono client calls same-origin
/apiroutes. Vite proxies those requests to the local API during development. - During SSR, a separate Hono client calls the API Worker directly through the
APIservice binding, avoiding a public network round trip. - The server Hono and Better Auth clients forward the incoming cookie header so authenticated requests retain the user's session.
- Server layout loads use the Better Auth client to resolve sessions and redirect users before rendering protected or auth-only pages.
The browser client is implemented in apps/web/src/lib/api.ts. Server-side API and authentication clients live in apps/web/src/lib/server/createServerApi.ts and apps/web/src/lib/server/createServerAuthClient.ts.
Wrangler generates the web Worker's binding declarations in apps/web/worker-configuration.d.ts. The web TypeScript configuration includes those declarations so SvelteKit's App.Platform can use Cloudflare.Env directly. Regenerate them after changing apps/web/wrangler.jsonc:
pnpm web:typegen:cfDatabase schema files live in apps/api/src/database/schema. After changing them, generate and apply a migration:
pnpm api:db:generate
pnpm api:db:migrateapi:db:generate also regenerates the Better Auth schema before creating the Drizzle migration. Review generated migration files before applying them to shared or production databases.
Both applications are configured as Cloudflare Workers:
apps/api/wrangler.jsoncdeploys the API and binds PostgreSQL through Hyperdrive.apps/web/wrangler.jsoncdeploys SvelteKit and binds the API Worker as theAPIservice.
Before deploying your own application:
- Replace the Worker names, routes, zone names, and Hyperdrive ID in both Wrangler files.
- Configure the API's production Better Auth values and web origin as Cloudflare secrets or variables.
- Ensure the web Worker's
APIservice binding matches the deployed API Worker name. - Apply migrations to the production PostgreSQL database.
- Authenticate Wrangler with
pnpm --filter api exec wrangler loginif needed.
Deploy the API first, followed by the web app:
pnpm api:deploy
pnpm web:deployAt minimum, update the following before using this repository for a new project:
- Root package name in
package.json - Worker names and routes in both
wrangler.jsoncfiles - Hyperdrive binding and database connection
BETTER_AUTH_APP_NAME, authentication URLs, and trusted web origin- Landing page, metadata, and example dashboard route
MIT