- apps/
- web/hero landing, next
- dashboard/better-auth login and app shell, next
- api/orpc on bun
- docs/optional module, fumadocs on next
- packages/
- ui/shadcn components and theme tokens
- common/shared zod schemas, constants and utils
- orpc/procedures and clients, built on common
- db/drizzle schema, migrations, seed scenario
- auth/better-auth setup
- env/zod-validated env, one root .env
- config/shared config
- storage/optional module
- ai/optional module
- docker/local postgres compose file
- .envgenerated, with secrets filled in
- turbo.json
Core and modules
The core is fixed and always generated: the apps api, web and dashboard, and the packages ui, orpc, db, auth, env, common and config. Everything else is a module: storage, AI, docs, notifications, analytics and so on. Modules are grouped, and the minimal, standard and everything profiles are just named sets of them, so you start from one and tweak.
One rule for the apps
apps/api is the only app that touches the database, auth, storage and providers. web and dashboard only call the api, so secrets and data access live in one place and the two frontends stay thin.
Shared code lives in common
packages/common holds what more than one app or package needs: zod schemas and their inferred types, constants such as input limits, and small utils. The api validates with the same schemas the dashboard forms read their limits from, so a rule changes in one file. Import from @your-scope/common/schemas, /constants or /utils; don't redeclare a type in an app when one exists there.
Commands
| Command | What it does |
|---|---|
pnpm dev | all apps |
pnpm dev:web | web and api |
pnpm dev:dashboard | dashboard and api |
pnpm build | build everything with turbo |
pnpm check | biome format and fix |
pnpm lint | biome and oxlint, no fixes |
pnpm check-types | typescript across the repo |
pnpm db:up / db:down | local postgres |
pnpm db:generate / db:migrate / db:seed / db:studio | drizzle |
pnpm ui:add <component> | add a shadcn component to packages/ui |
Environment
One .env at the root feeds every app, validated by packages/env. The cli generates it from .env.example with secrets filled in. Keys for modules you turned off are removed.