|
| 1 | +# Database migrations |
| 2 | + |
| 3 | +This project uses a **versioned migration framework** for PostgreSQL. Schema changes are applied deterministically and can be rolled back when needed. |
| 4 | + |
| 5 | +## Quick reference |
| 6 | + |
| 7 | +| Task | Command | |
| 8 | +|------|--------| |
| 9 | +| Apply pending migrations | `cd backend && npm run db:migrate` | |
| 10 | +| Preview (dry-run) | `cd backend && npm run db:migrate -- --dry-run` | |
| 11 | +| Roll back last migration | `cd backend && npm run db:migrate -- --rollback` | |
| 12 | +| Roll back last N migrations | `cd backend && npm run db:migrate -- --rollback 2` | |
| 13 | +| Show status | `cd backend && npm run db:migrate -- --status` | |
| 14 | + |
| 15 | +Requires `DATABASE_URL` in the environment (e.g. in `.env` or CI). |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +## Backup and rollback |
| 20 | + |
| 21 | +### Before running migrations |
| 22 | + |
| 23 | +1. **Back up the database** (recommended for production): |
| 24 | + - **PostgreSQL:** `pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql` |
| 25 | + - Or use your provider’s snapshot/backup (e.g. RDS snapshot, Heroku pg:backups). |
| 26 | + |
| 27 | +2. **Dry-run** to see what will run: |
| 28 | + ```bash |
| 29 | + npm run db:migrate -- --dry-run |
| 30 | + ``` |
| 31 | + |
| 32 | +3. Run migrations: |
| 33 | + ```bash |
| 34 | + npm run db:migrate |
| 35 | + ``` |
| 36 | + |
| 37 | +### If a migration fails |
| 38 | + |
| 39 | +1. Fix the failure (e.g. fix SQL, fix data, or fix environment). |
| 40 | +2. If you need to **undo the last migration**: |
| 41 | + ```bash |
| 42 | + npm run db:migrate -- --rollback |
| 43 | + ``` |
| 44 | + This runs the corresponding `.down.sql` and removes the row from `schema_migrations`. |
| 45 | + |
| 46 | +3. Restore from backup if you need to restore data: |
| 47 | + ```bash |
| 48 | + psql $DATABASE_URL < backup_YYYYMMDD_HHMMSS.sql |
| 49 | + ``` |
| 50 | + |
| 51 | +### Rollback by migration |
| 52 | + |
| 53 | +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: |
| 54 | + |
| 55 | +| Migration | Rollback (down) | |
| 56 | +|-----------|------------------| |
| 57 | +| `001_initial_schema` | Drops `notification_preferences`, `analytics_snapshots`, `rebalance_events`, `portfolios` (in that order). | |
| 58 | +| `002_seed_demo_data` | Deletes demo portfolio `demo-portfolio-1` and its rebalance events. | |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Seed / demo data migration path |
| 63 | + |
| 64 | +- **Optional migration:** `002_seed_demo_data` inserts a demo portfolio and sample rebalance events. It is **idempotent** (safe to run multiple times; uses `ON CONFLICT DO NOTHING`). |
| 65 | +- **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. |
| 66 | +- **To apply only schema (no demo data):** Ensure `002_seed_demo_data` is not applied (e.g. use a separate DB for prod and run only `001_initial_schema`, or roll back `002` after seeding a staging DB if you prefer). |
| 67 | +- **SQLite (local):** The backend also supports SQLite via `DB_PATH`. Demo data is seeded automatically by `DatabaseService` when the DB is empty; there is no separate migration runner for SQLite. For schema changes that affect both PostgreSQL and SQLite, update: |
| 68 | + - `backend/src/db/migrations/` (PostgreSQL) |
| 69 | + - `backend/src/services/databaseService.ts` `SCHEMA_SQL` (SQLite) |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +## Version history and CI |
| 74 | + |
| 75 | +- **Version history** is stored in the `schema_migrations` table (`version`, `name`, `applied_at`). |
| 76 | +- **Migrations live in the repo** under `backend/src/db/migrations/` with naming: |
| 77 | + - `NNN_description.up.sql` – forward migration |
| 78 | + - `NNN_description.down.sql` – rollback for that version |
| 79 | +- **Deterministic order:** Migrations run in ascending order of `NNN`. The same list of files produces the same order in every environment. |
| 80 | +- **CI:** The backend workflow can run `npm run db:migrate -- --dry-run` to 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). |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## Adding a new migration |
| 85 | + |
| 86 | +1. Add two files in `backend/src/db/migrations/`: |
| 87 | + - `003_short_description.up.sql` – forward SQL |
| 88 | + - `003_short_description.down.sql` – rollback SQL |
| 89 | +2. Use the next sequential number; do not renumber existing migrations. |
| 90 | +3. Document rollback behavior in this file if it’s non-obvious. |
| 91 | +4. Run `npm run db:migrate -- --dry-run` to confirm, then apply with `npm run db:migrate`. |
0 commit comments