This project uses a versioned migration framework for PostgreSQL. Schema changes are applied deterministically and can be rolled back when needed.
| Task | Command |
|---|---|
| Apply pending migrations | cd backend && npm run db:migrate |
| Preview (dry-run) | cd backend && npm run db:migrate -- --dry-run |
| Roll back last migration | cd backend && npm run db:migrate -- --rollback |
| Roll back last N migrations | cd backend && npm run db:migrate -- --rollback 2 |
| Show status | cd backend && npm run db:migrate -- --status |
Requires DATABASE_URL in the environment (e.g. in .env or CI).
-
Back up the database (recommended for production):
- PostgreSQL:
pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql - Or use your provider’s snapshot/backup (e.g. RDS snapshot, Heroku pg:backups).
- PostgreSQL:
-
Dry-run to see what will run:
npm run db:migrate -- --dry-run
-
Run migrations:
npm run db:migrate
-
Fix the failure (e.g. fix SQL, fix data, or fix environment).
-
If you need to undo the last migration:
npm run db:migrate -- --rollback
This runs the corresponding
.down.sqland removes the row fromschema_migrations. -
Restore from backup if you need to restore data:
psql $DATABASE_URL < backup_YYYYMMDD_HHMMSS.sql
Each migration has a down file (e.g. 001_initial_schema.down.sql) that reverses the up migration. The runner applies down migrations in reverse order when you use --rollback [n]. Documented rollback behavior:
| Migration | Rollback (down) |
|---|---|
001_initial_schema |
Drops notification_preferences, analytics_snapshots, rebalance_events, portfolios (in that order). |
002_seed_demo_data |
Deletes demo portfolio demo-portfolio-1 and its rebalance events. |
- Optional migration:
002_seed_demo_datainserts a demo portfolio and sample rebalance events. It is idempotent (safe to run multiple times; usesON CONFLICT DO NOTHING). - When to use: Development, staging, or demo environments. You can skip this migration in production by not running it, or run it once for a demo instance.
- To apply only schema (no demo data): Ensure
002_seed_demo_datais not applied (e.g. use a separate DB for prod and run only001_initial_schema, or roll back002after seeding a staging DB if you prefer). - SQLite (local): The backend also supports SQLite via
DB_PATH. Demo data is seeded automatically byDatabaseServicewhen the DB is empty; there is no separate migration runner for SQLite. For schema changes that affect both PostgreSQL and SQLite, update:backend/src/db/migrations/(PostgreSQL)backend/src/services/databaseService.tsSCHEMA_SQL(SQLite)
- Version history is stored in the
schema_migrationstable (version,name,applied_at). - Migrations live in the repo under
backend/src/db/migrations/with naming:NNN_description.up.sql– forward migrationNNN_description.down.sql– rollback for that version
- Deterministic order: Migrations run in ascending order of
NNN. The same list of files produces the same order in every environment. - CI: The backend workflow can run
npm run db:migrate -- --dry-runto verify migration files and that the runner works. For full reproducibility, run real migrations in CI against a Postgres service container and then run tests (see workflow example below).
- Add two files in
backend/src/db/migrations/:003_short_description.up.sql– forward SQL003_short_description.down.sql– rollback SQL
- Use the next sequential number; do not renumber existing migrations.
- Document rollback behavior in this file if it’s non-obvious.
- Run
npm run db:migrate -- --dry-runto confirm, then apply withnpm run db:migrate.