A modern, type-safe monorepo template for building full-stack applications with Next.js, tRPC, Drizzle ORM, and a Cloudflare-native background-job stack (Workflows + Queues + Durable Objects).
- Bun (v1.1.42+) - Install Bun
- PostgreSQL Database - Use Neon, Supabase, or any PostgreSQL provider
- Cloudflare Account - Sign up β Workers Paid plan is required for Workflows + Durable Objects
- Sentry Account - Sign up for error tracking
- PostHog Account - Sign up for analytics (optional)
-
Create a new repository from this template:
# Use GitHub's "Use this template" button, or: gh repo create my-app --template stevepeak/kyoto-template -
Clone your new repository:
git clone <your-repo-url> cd my-app
-
Install dependencies:
bun install
-
Set up environment variables: Create a
.envfile in the root directory:# App Configuration APP_URL="http://localhost:3000" # Database (PostgreSQL) DATABASE_URL="postgresql://user:password@host:5432/database" # Better Auth BETTER_AUTH_SECRET="your-secret-here" # Generate with: openssl rand -base64 32 # Cloudflare Workflows worker (replaces Trigger.dev) TOKEN_SIGNING_KEY="..." # Generate with: openssl rand -base64 32 WORKFLOWS_URL="http://localhost:8787" NEXT_PUBLIC_WORKFLOWS_URL="http://localhost:8787" # AI β routed through OpenRouter OPENROUTER_API_KEY="sk-or-v1-xxxxx" # Sentry (Error Tracking) SENTRY_DSN="https://xxxxx@sentry.io/xxxxx" # PostHog (Analytics - optional) POSTHOG_API_KEY="ph_xxxxx" POSTHOG_HOST="https://us.i.posthog.com"
-
Set up the database:
# Generate migration files from schema bun --cwd packages/db db:generate # Apply migrations to database bun --cwd packages/db db:migrate # Or push schema directly (for development) bun --cwd packages/db db:push
-
Start development servers:
# Start all apps (web + workflows worker) bun run dev # Or start just the web app bun run dev:web
- Web app: http://localhost:3000
- Workflows worker: http://localhost:8787 (Wrangler dev with local
Workflows / Queues / Durable Object simulation, persisted to
.wrangler/state)
kyoto-template/
βββ apps/
β βββ web/ # Next.js 16 web application
β β βββ app/ # App Router pages and routes
β β βββ ...
β βββ workflows/ # Cloudflare Workflows / Queues / DOs worker
β βββ src/
β βββ workflows/ # WorkflowEntrypoint classes
β βββ durable-objects/ # Per-run progress / WebSocket fan-out
β βββ queues/ # Queue consumers
β
βββ packages/
β βββ api/ # tRPC API definitions
β β βββ src/
β β βββ index.ts # Main router export
β β βββ trpc.ts # tRPC setup
β β
β βββ db/ # Drizzle ORM database package
β β βββ src/
β β β βββ schema.ts # Database schema definitions
β β β βββ migrate.ts # Migration runner
β β βββ migrations/ # Generated migration files
β β
β βββ config/ # Environment variable validation
β β βββ src/index.ts # Zod-validated config
β β
β βββ agents/ # AI agent tooling and MCP integration
β βββ utils/ # Shared utilities
β βββ posthog/ # PostHog analytics helpers
β
βββ config/ # Shared configuration
βββ eslint/ # ESLint config
βββ tsconfig/ # TypeScript configs
bun run dev- Start all development serversbun run dev:web- Start only the web appbun run build- Build all packages and appsbun run typecheck- Type check all packagesbun run lint- Lint all packagesbun run fix- Auto-fix linting issuesbun run ci- Run CI checks (typecheck + lint + knip + build)bun run clean- Clean all build artifacts
bun --cwd packages/db db:generate- Generate migration filesbun --cwd packages/db db:migrate- Run migrationsbun --cwd packages/db db:push- Push schema directly (dev only)bun --cwd packages/db db:studio- Open Drizzle Studio
bun --cwd apps/workflows dev- Start Wrangler dev with Workflows / Queues / DOsbun --cwd apps/workflows dev:scheduled- Same, plus the/__scheduledendpoint for manual cron firingbun --cwd apps/workflows deploy- Deploy the worker (Workflows + DOs + Queues + Cron triggers)bun --cwd apps/workflows tail- Stream production logs
Trigger a cron run locally:
curl 'http://localhost:8787/__scheduled?cron=0+3+*+*+*'- Bun - Fast JavaScript runtime and package manager
- Turborepo - High-performance monorepo build system
- TypeScript - Type-safe JavaScript
- Next.js 16 - React framework with App Router
- React 19 - UI library
- Tailwind CSS v4 - Utility-first CSS
- tRPC - End-to-end typesafe APIs
- Drizzle ORM - Type-safe SQL ORM
- PostgreSQL - Database
- Cloudflare Workflows - Durable, resumable async tasks (replaces Trigger.dev)
- Cloudflare Queues - Batched fire-and-forget jobs with retries + DLQ
- Cloudflare Durable Objects - Per-run progress fan-out via WebSocket / SSE
- Zod - Schema validation
- Sentry - Error tracking and monitoring
- PostHog - Product analytics
- Vercel - Deployment (configured in
vercel.json)
Centralized environment variable parsing and validation with Zod. Provides type-safe access to all environment variables across the monorepo.
import { getConfig } from '@app/config'
const { APP_URL, DATABASE_URL } = getConfig()Server-side tRPC API definitions. Contains all API routers and procedures with end-to-end type safety.
Drizzle ORM database package. Contains schema definitions, migrations, and database connection logic.
import { db } from '@app/db'
import { schema } from '@app/db/schema'AI agent tooling and MCP (Model Context Protocol) integration for orchestrating specialized agents.
Shared JavaScript utilities and helper functions.
Adjust types and schemas first before implementing logic. Run typecheck after changes:
bun run typecheckUse named arguments instead of inline arguments:
// β
Good
function greet(args: { name: string }) {}
// β Avoid
function greet(name: string) {}- NEVER edit files in
packages/db/migrations - ONLY edit
packages/db/src/schema.tsto make schema changes - Use
db:generateto create migrations anddb:migrateto apply them
- Uses ESLint and Prettier for code formatting
- Uses Knip for detecting unused code
- Pre-commit hooks with Husky and lint-staged
The project is configured for Vercel deployment. Set all environment variables in the Vercel dashboard.
Deploy the workflows Worker (Workflows + Durable Objects + Queues + Cron triggers in one shot):
bun --cwd apps/workflows deploySet runtime secrets once:
bunx wrangler --cwd apps/workflows secret put TOKEN_SIGNING_KEY
bunx wrangler --cwd apps/workflows secret put OPENROUTER_API_KEY
bunx wrangler --cwd apps/workflows secret put SENTRY_DSNThe CI workflow (.github/workflows/ci.yml) runs database migrations and deploys to Cloudflare on every push to main. Add the following secrets at GitHub β Settings β Secrets and variables β Actions β New repository secret:
| Secret | Used by | Where to find it |
|---|---|---|
DATABASE_URL |
deploy-migrations |
Your PostgreSQL provider's dashboard. For Neon, open the project β Connection Details β copy the pooled connection string. For Supabase, open Project Settings β Database β Connection string (URI). |
CLOUDFLARE_API_TOKEN |
deploy-cloudflare |
Cloudflare dashboard β My Profile β API Tokens β Create Token β use the Edit Cloudflare Workers template (or a custom token with Account: Workers Scripts: Edit + User: User Details: Read). |
CLOUDFLARE_ACCOUNT_ID |
deploy-cloudflare |
Cloudflare dashboard β select your account β Workers & Pages β the Account ID is shown in the right sidebar (or in the URL: dash.cloudflare.com/<account-id>). |
NEXT_PUBLIC_APP_URL |
deploy-cloudflare |
Public URL of the deployed app (e.g. https://kyoto-web.example.workers.dev). Inlined into the client bundle at build time. |
NEXT_PUBLIC_POSTHOG_API_KEY |
deploy-cloudflare |
PostHog β Project Settings β Project API Key. Inlined into the client bundle at build time so the browser SDK can initialize. |
NEXT_PUBLIC_POSTHOG_HOST |
deploy-cloudflare |
https://us.i.posthog.com (US cloud) or https://eu.i.posthog.com (EU). Inlined at build time. |
NEXT_PUBLIC_SENTRY_DSN |
deploy-cloudflare |
Sentry β Project Settings β Client Keys (DSN). Inlined at build time. |
You can also add them via the GitHub CLI:
gh secret set DATABASE_URL
gh secret set CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID
gh secret set NEXT_PUBLIC_APP_URL
gh secret set NEXT_PUBLIC_POSTHOG_API_KEY
gh secret set NEXT_PUBLIC_POSTHOG_HOST
gh secret set NEXT_PUBLIC_SENTRY_DSNServer-side secrets (the ones without a NEXT_PUBLIC_ prefix β POSTHOG_API_KEY, SENTRY_DSN, OPENROUTER_API_KEY, BETTER_AUTH_SECRET, etc.) are read by the Worker at runtime, not inlined at build. Set them once with Wrangler from apps/web:
bunx wrangler secret put POSTHOG_API_KEY
bunx wrangler secret put POSTHOG_HOST
bunx wrangler secret put SENTRY_DSN
bunx wrangler secret put BETTER_AUTH_SECRET
bunx wrangler secret put DATABASE_URL
# ...repeat for any other required secret in packages/config/src/index.tsThe CI deploy uses wrangler deploy --keep-vars so these secrets persist across deployments.
- Next.js Documentation
- tRPC Documentation
- Drizzle ORM Documentation
- Cloudflare Workflows Documentation
- Cloudflare Queues Documentation
- Cloudflare Durable Objects Documentation
- Turborepo Documentation
See LICENSE file for details.