Database
apps/web owns the database, and its schema is assembled from three places.
packages/layer-payments/server/db/schema/ declares the payments tables,
packages/layer-base/server/db/schema/ supplies the shared column mixins they are built from, and
@nuxtjs/better-auth generates its own tables into .nuxt/better-auth/ while Nuxt prepares the
application. NuxtHub bundles all of them into the one schema drizzle-kit reads.
Commands
Run these from apps/web.
| Command | Does |
|---|---|
pnpm db:generate | writes a new migration from the current schema |
pnpm db:check | checks the migration lineage for collisions |
pnpm db:migrate:production | applies committed migrations to the database |
Migrations are written to apps/web/server/db/migrations/postgresql/. Commit them.
When migrations are applied
The deploy workflow applies them for apps/web only, on a push, after the build and before the
Worker is uploaded — so a Worker never serves against a schema that is behind it.
pnpm db:migrate:production refuses to do anything unless RUN_DB_MIGRATIONS is true, and it needs
DATABASE_URL to be the direct, non-pooled connection string. Hyperdrive pools connections
inside the Worker runtime and does not exist outside it.
One lineage, generated once
Generate a migration once on development, commit it, and apply it. staging applies those same
committed migrations and never generates one of its own. Two branches generating against the same
schema produce two histories that each describe a database the other never had.
Your copy has no migrations
NuxtStart ships you the schema, not its own migration history. You generate your first migration
from that schema and own the lineage from there. See
Migration publication for why, and for what it does to your
git log.
