40227-vm/backend/docs/migrations-and-seeders.md
2026-06-19 16:43:41 +02:00

5.4 KiB

Migrations And Seeders Backend

Purpose

Schema creation and reference-data seeding run through a small Umzug runner (it replaced sequelize-cli during the TypeScript/ESM migration). This doc covers the developer mechanism: the runner, file conventions, and how to add a migration or seeder. For VM/PM2 operational use (when migrate/seed run on deploy, db:reset recovery), see ../../docs/deployment-vm.md.

Files

  • Runner: src/db/umzug.ts — builds two Umzug instances (migrator, seeder) and a small CLI.
  • Reset: src/db/reset.ts — drops every table in the public schema, then re-runs migrator.up() + seeder.up().
  • Schema snapshot: src/db/initial-schema.ts — DDL snapshot derived from the Sequelize models; the models remain the source of truth.
  • Migrations: src/db/migrations/*.ts20260610000000-initial-schema.ts (creates the full schema from the snapshot) and 20260610010000-add-role-scope-and-user-campus.ts (adds the NOT-NULL roles.scope enum and the nullable users.campusId). Phase 4 adds 20260611000000-policy-documents-and-acknowledgments.ts (the unified policy store + per-version acknowledgments), 20260611010000-audio-files.ts (the audio library) + 20260611060000-audio-files-kinds.ts (the kind enum / nullable url / recipe JSONB), and 20260611040000-add-user-name-prefix.ts (the users.name_prefix honorific enum), and 20260611070000-campuses-timezone.ts (the required campuses.timezone IANA column — added nullable, backfilled, then set NOT NULL), and 20260612000000-frame-entries-week-label.ts (the optional frame_entries.week_label; week_of is now the canonical Sunday-start ISO date).
  • Seeders: src/db/seeders/[0-9]*.tsadmin-user (the system users, the primary tenant's per-role users, and the secondary tenant's per-role users from shared/constants/seed-fixtures.ts), user-roles (the first-class roles, the permission catalog incl. product-feature permissions, the role->permission matrix, role assignment by user id), product-campuses, content-catalog (+ payloads under seeders/content-catalog-data/), rbac-fixtures (the two companies, school/campus ownership, per-user org/school/campus links, and user employment fields), class-fixtures (one class, enrollment, and guardian link per tenant), and 20260611050000-policy-documents-seed.ts (3 safety protocols + 4 handbook policies). Shared fixture definitions live in src/shared/constants/seed-fixtures.ts.

Mechanism

  • migrator globs migrations/*.{ts,js}; history is tracked in the default SequelizeMeta table via SequelizeStorage.
  • seeder globs seeders/[0-9]*.{ts,js}; history is tracked in a separate SequelizeData model/table. The runner sets both modelName and tableName to SequelizeData, which prevents Umzug from reusing the default SequelizeMeta model when seeders run after migrations in the same process. Only timestamped seeder files are loaded; colocated *.test.ts files are intentionally excluded from db:seed.
  • Each file is ESM TypeScript with a default export { up, down }, each taking (queryInterface, Sequelize). The runner accepts either a default export or top-level up/down.
  • Tracked names strip the .ts/.js extension, so history is stable whether the runner is executed via tsx (dev, .ts) or compiled (prod, dist/.../*.js).
  • The glob accepts both .ts and .js, so already-applied entries are not re-run after a build.
  • Seeders are append/idempotent: when a seeded row or relationship already exists, the seeder preserves it and inserts only missing rows/links. This lets db:seed recover from incomplete SequelizeData history without deleting tenant-edited content or failing on duplicate keys.

CLI And Scripts

src/db/umzug.ts exposes: migrate:up, migrate:down, migrate:pending, seed:up, seed:down. npm scripts wrap them:

  • Dev (via tsx): db:migrate (migrate:up), db:migrate:undo (migrate:down), db:migrate:pending, db:seed (seed:up), db:seed:undo (seed:down), db:reset (tsx src/db/reset.ts).
  • Prod (compiled, no tsx): db:migrate:prod (node dist/db/umzug.js migrate:up), db:seed:prod (node dist/db/umzug.js seed:up).

Authoring A New Migration / Seeder

  1. Add src/db/migrations/<timestamp>-<name>.ts (or seeders/<timestamp>-<name>.ts) exporting export default { up, down } with typed (queryInterface, Sequelize) signatures.
  2. For seeders, use stable natural keys (id, name, importHash, or a junction pair) and insert only the missing rows. Do not delete/reinsert tenant-editable seed content in up.
  3. Run npm run db:migrate (or db:seed) in dev; verify with db:migrate:pending.
  4. Regenerate database-schema.md after any schema change (it is generated from the models).

Tests

  • src/shared/constants/seed-fixtures.test.ts covers primary/secondary tenant user topology and credential uniqueness.
  • src/db/umzug.test.ts covers the dedicated SequelizeData storage contract for seeder history and the timestamp-only seeder glob.
  • src/db/seeders/user-roles.test.ts covers the seeded product-permission contract for parent communication and registrar report/audit grants.
  • database-schema.md (the generated schema reference), backend-architecture.md (DAL layer), ../../docs/deployment-vm.md (operational migrate/seed on the VM).