Migrations
Create, apply and reset database migrations, and seed a new database.
Litestore builds and changes its database through committed migrations. Read this page before you change the schema, set up a new database, or deploy to production.
Migrations and guarantees
prisma/migrations/ contains the committed migration history. The first migration is a baseline that creates the whole schema and installs the money, refund and stock guarantees, and every later migration changes the schema from there.
Litestore needs migrations because the money guarantees are CHECK constraints, triggers and views, and a push that only compares schemas would never create them. The baseline migration is the only thing that installs them.
If you used prisma db push instead, you'd get every table and column in this repository, but the database would accept a refund larger than the order it refunds.
Migration files
Baseline migration
The 20260726143000_baseline_with_db_guarantees migration is 3,121 lines long.
It first creates the schema from scratch, including 19 enum types and all tables with their indexes, and then installs the parts that a schema diff can't produce.
| What it installs | Details |
|---|---|
| CHECK constraints | The migration adds 20 of them. |
| Trigger functions | The migration adds four litestore_guard_* functions, with their triggers. |
| Case-insensitive unique indexes | Examples are user_email_lower_unique, product_variant_sku_upper_unique and product_slug_lower_unique. |
| Views | The migration creates six views, listed below. |
OrderMoneyTotals
OrderMoneyDrift
CustomerMoneyTotals
CustomerMoneyDrift
InventoryStockTotals
InventoryLedgerDriftPerformance indexes
The 20260730090000_performance_indexes migration has three statements, and each one is a CREATE INDEX IF NOT EXISTS:
OrderItem_variantId_idx
Customer_createdAt_idx
Media_url_idxLater migrations
Every migration after these two is a small, named step, and the names say what changed: for example 20261012000000_pages_content_drop_blocks_ads_social moved storefront pages to HTML content and dropped the block, ad and social tables, 20261013000000_order_cancelled_at added the cancellation timestamp, and 20261014000000_settings_to_code removed the settings that became constants in platform/config/. Some of them also backfill or correct data, so apply them in order with db:deploy.
Partial unique indexes
The baseline creates partial unique indexes that Prisma's schema language can't express, such as channel_one_default_active_unique (one active default channel). Prisma therefore always sees them as drift, and db:migrate proposes DROP INDEX lines for them. Delete those lines from every new migration before you apply it. The comment at the top of 20261011000000_catch_up_indexes/migration.sql explains this. (The second index it names, on EmailTemplate, went away with that table.)
Other files
prisma/migrations/migration_lock.toml records provider = "postgresql". Litestore doesn't use Prisma TypedSQL, so there is no prisma/sql/ folder.
Commands
| Command | What it runs | When you use it |
|---|---|---|
bun run db:migrate | prisma migrate dev --create-only | You changed the schema locally. It writes a new migration into prisma/migrations/ but doesn't apply it, so the SQL is there to read and edit first. |
bun run db:deploy | prisma migrate deploy | You apply migrations locally after db:migrate, and in production. It only applies pending migrations, without creating migrations, prompting you or resetting the database. |
bun run db:reset | prisma migrate reset | You rebuild a local database. It drops the database, replays every migration, and then runs the seed. |
bun run db:seed | bun prisma/seed.ts | You seed the database without dropping anything. |
bun run db:generate | prisma generate | You regenerate the client after a schema change that you didn't migrate yourself. |
No script runs prisma db push, and you shouldn't run it yourself, because it skips the constraints, triggers and views that only the migrations install.
db:reset drops the database
prisma migrate reset deletes every row before it replays the migrations. Only use it on a local development database. If you point it at a DATABASE_URL that contains real orders, it destroys them.
Schema changes
Schema edit
Make your change in prisma/schema.prisma.
Migration file
bun run db:migrateSQL review
Read the SQL before you commit it, and delete any DROP INDEX lines for the partial unique indexes.
Apply and test
bun run db:deploy
bun run test:integrationdb:deploy applies the new migration to your local database. If the migration touches money, refunds or stock, run the integration tests too. test/integration/db-guarantees.test.ts checks that Postgres still rejects the writes the guarantees forbid. The test database needs the migration as well, so apply it there before you run the tests.
Seeding
prisma/seed.ts is a 2,211-line script. It builds its own PrismaClient over PrismaPg using process.env.DATABASE_URL instead of importing ~/platform/db/client, so it runs without the soft-delete extension. It refuses to run when NODE_ENV is production unless you set SEED_ALLOW_PRODUCTION=1.
Almost every write is an upsert keyed on a natural key, so running the script again doesn't create duplicate rows. According to the summary the script prints at the end, it creates two rows for each of 41 base models, plus 20 additional products with 80+ variants.
Skipped data
The seed doesn't create orders. It prints this message instead:
Skipping order seeding (orders created through checkout)It also skips refund requests, refund request items, returns, coupon usage, shipments and manual orders. These rows should only be created by the flows that normally produce them, because the money constraints apply to them. The constraints assume an order came from a checkout, so an order inserted directly by the seed wouldn't match the data they require.
Admin account
The seed creates an admin account for admin@example.com.
You sign in with a magic link. Outside production the email, including the link, is printed in the terminal running bun run dev instead of being sent; in production Resend delivers it.
Seed command
bun run db:seed runs bun prisma/seed.ts. prisma.config.ts sets migrations.seed to the same command, which is what prisma migrate reset runs after it replays the migrations.
Quick reference
All four answers come from the same rule: the database only enforces guarantees that a migration has installed.
Related
Schema
The core Prisma models follow shared conventions, and schema changes follow a few rules to stay safe.
Guarantees
Postgres enforces some guarantees, application code enforces others, and Litestore doesn't make the rest.
Client
Litestore builds a single Prisma client, and its connection pool settings each have a reason.