Migrations & Seeds
Every time you need to change the database schema — add a table, add a column, modify a constraint — you write a migration. Every time you need consistent starting data (default roles, plans, admin users), you write a seed. Both use the same TypeScript file format and are run via Makefile commands inside Docker.The file format
Both migrations and seeds export two functions:up (apply the change) and down (reverse it). They receive Sequelize’s queryInterface via the context object:
- Column names use
snake_case— that’s what Sequelize maps tocamelCasein models. - Always include
created_atandupdated_aton new tables. - Always write a
downfunction, even if it just drops the table. You’ll thank yourself when you need to roll back.
Make commands
Adding a new table (step by step)
-
Generate the migration file
This creates a timestamped file in
apps/backend/src/tools/rds/sequelize/migrations/. Open it and fill in theupanddownfunctions. -
Write the migration
Use
queryInterface.createTableinupandqueryInterface.dropTableindown: -
Run the migration
-
Create a Sequelize model
Add a new file in
apps/backend/src/tools/rds/sequelize/models/widget.tsfollowing the pattern of existing models. -
Register the model
Add it to the models index in
apps/backend/src/tools/rds/sequelize/models/index.ts. -
Verify
The
globalSetupin Jest runsmigrate-upautomatically before tests, so your new table will be present in the test database.
Adding a column to an existing table
For schema changes to existing tables, useaddColumn / removeColumn:
⚠️ Watch out: if you’re adding aNOT NULLcolumn to a table that already has rows, you need to either provide adefaultValueor do it in two steps: add the column as nullable, backfill data, then add aNOT NULLconstraint. Doing it in one step on a table with existing data will fail.
Seeds
Seeds are for data that should always exist — default roles, product plans, an initial admin user. They follow the sameup / down format:
make fresh-start runs both migrate-up and seed-up in sequence.
What’s next?
- Sequelize tooling — how the Sequelize model layer is set up.
- Monorepo — workspace structure and how packages reference each other.
- Testing — how Jest’s globalSetup automatically applies migrations before tests.