For AI agents: fetch the complete documentation index at /llms.txt, the full documentation corpus at /llms-full.txt, the agent skill at /skill.md, or this page as Markdown at /docs/web/database.md (or send Accept: text/markdown).
Database and migrations
Prepare Postgres and evolve the Drizzle schema with committed migrations.
Setup and configuration
Before any step, read Before you start and Terminal basics. These instructions assume you opened the full supastack monorepo in VS Code; its root package.json marks the repository root. In pnpm commands, a workspace package is an app or shared-code folder; in the product, a workspace is a team's data container and security boundary. corepack pnpm --filter ... selects one package from the monorepo while commands run from its root. If you opened only a synced mobile or desktop folder, it does not contain the Web backend, Docker setup, or root scripts: run the backend from this monorepo or a reachable hosted server, and use the platform folder's own scripts. An environment variable is a named setting stored in a local .env file or your hosting dashboard; restart the relevant app after changing it, and keep server secrets out of NEXT_PUBLIC_ and EXPO_PUBLIC_ values.
1. Install and start Docker Desktop.
2. Copy .env.example to .env in the root for migration tools and to apps/web/.env.local for Next.js (PowerShell: Copy-Item .env.example .env; Copy-Item .env.example apps/web/.env.local. macOS/Linux/Git Bash: cp .env.example .env; cp .env.example apps/web/.env.local). Keep DATABASE_URL the same in both files; the example already matches the local Docker database.
3. From the repository root run docker compose up -d postgres and docker compose ps; wait until postgres reports healthy.
4. Run corepack pnpm --filter @supastack/db db:migrate from the root to apply the migrations already shipped with the kit. Only after editing packages/db/src/schema.ts, run db:generate, inspect the generated SQL, then run db:migrate. Expected: the local Postgres container is healthy and the Web app uses its migrated database. Never point this first run at production.
Configure DATABASE_URL for the intended Postgres database in the server environment. Use a separate disposable database for integration acceptance. Never aim test migrations at a production database.
The database workspace exports schema definitions and persistence helpers. Review packages/db/src/schema.ts and the migration directory before introducing a table or column.
docker compose up -d postgres
docker compose ps
corepack pnpm --filter @supastack/db db:migrate
# Only after changing packages/db/src/schema.ts:
corepack pnpm --filter @supastack/db db:generate
corepack pnpm --filter @supastack/db db:migrate
# Optional Compose / Supabase settings in .env:
# POSTGRES_PASSWORD=supastack
# SUPABASE_ACCESS_TOKEN=
# DATABASE_POOLER=transactionCommands to copy
Follow the comments to see which folder a command runs in. If two terminals are labeled, open two terminal windows and keep the first command running while you use the second.
# Choose one copy method for your terminal:
# PowerShell:
Copy-Item .env.example .env
Copy-Item .env.example apps/web/.env.local
# macOS/Linux/Git Bash alternative:
# cp .env.example .env
# cp .env.example apps/web/.env.local
docker compose up -d postgres
docker compose ps
corepack pnpm --filter @supastack/db db:migrateImplementation workflow
Generate a migration after editing the schema, inspect the generated SQL, and commit it with the schema change. Plan how existing records receive values before adding a required column.
Include workspaceId on every user-owned record and carry that scope into reads and writes. Use immutable model-price history for AI accounting instead of rewriting prices used by existing usage records.
Verification and troubleshooting
If Docker says it cannot connect to its engine, start Docker Desktop and wait for it to finish loading. If postgres is not healthy, run docker compose logs postgres from the root and fix that error before migrating. If Web says DATABASE_URL is missing, check apps/web/.env.local; if db:migrate cannot connect, check the root .env. If you change databases, update both files. Read the first failed migration statement before retrying; never repeat it against production.
Apply migrations to an isolated database and run database-gated tests there. Missing credentials should produce an explicit skipped gate, not an assertion that persistence passed. If migration fails, inspect which statement failed and the current schema before retrying.