A TypeScript monorepo built with spec-driven development (OpenSpec). Stack: Nuxt 4 (UI + Nitro server) · oRPC · Drizzle ORM + MariaDB · pnpm workspaces.
apps/
web/ Nuxt 4 app — UI + Nitro server that hosts the oRPC API
packages/
api/ oRPC router, procedures, request context (@checkin/api)
db/ Drizzle schema, MariaDB client, migrations (@checkin/db)
openspec/ OpenSpec specs & change proposals
The backend is not a separate service: the oRPC router (packages/api) is mounted inside
Nuxt's Nitro server at apps/web/server/routes/rpc/[...].ts, and the typed client is provided by
apps/web/app/plugins/orpc.ts.
- Node.js 22 (
.nvmrc) - pnpm 10+
- Docker (for local MariaDB)
pnpm install
cp .env.example .env # required — DATABASE_URL must be set before `pnpm dev`
docker compose up -d # start MariaDB
pnpm db:migrate # apply migrations
pnpm dev # Nuxt dev server → http://localhost:3000The home page calls the health.check oRPC procedure to confirm the front → Nitro → oRPC wiring.
health.check doesn't query the database, but every oRPC request resolves context.db, so a
missing DATABASE_URL surfaces as a 500 on the home page — copy .env first.
| Command | Description |
|---|---|
pnpm dev |
Run the Nuxt app in dev mode |
pnpm build |
Build the Nuxt app (incl. Nitro server) |
pnpm lint |
ESLint across the monorepo |
pnpm typecheck |
Type-check every package |
pnpm db:generate |
Generate a Drizzle migration from the schema |
pnpm db:migrate |
Apply pending migrations |
pnpm db:push |
Push schema directly (dev only) |
pnpm db:studio |
Open Drizzle Studio |
Features are built spec-first. From Claude Code (or any supported assistant):
/opsx:propose "<what you want to build>"— generatesproposal.md,design.md,tasks.mdand updated specs underopenspec/changes/<name>/./opsx:apply— implement the tasks./opsx:archive— fold the change intoopenspec/specs/once shipped.
openspec list shows active changes. See OpenSpec and
openspec/project.md for project conventions.
- New oRPC procedure: add to
packages/api/src/router.ts(built frompub), grouped by capability. The client picks up types automatically viaAppRouter. - New table: define it in
packages/db/src/schema.ts, runpnpm db:generate, and commit the generated migration inpackages/db/drizzle/.