Tenant Migration Scripts
This directory contains scripts for managing database migrations across all tenants in the multi-tenant platform.
Available Scripts
1. Create a New Migration
npm run migrate:make <migration_name>
Creates a new migration file in migrations/tenant/ directory.
Example:
npm run migrate:make add_status_field_to_contacts
2. Migrate a Single Tenant
npm run migrate:tenant <tenant-slug-or-id>
Runs all pending migrations for a specific tenant. You can identify the tenant by its slug or ID.
Example:
npm run migrate:tenant acme-corp
npm run migrate:tenant cm5a1b2c3d4e5f6g7h8i9j0k
3. Migrate All Tenants
npm run migrate:all-tenants
Runs all pending migrations for all active tenants in the system. This is useful when:
- You've created a new migration that needs to be applied to all tenants
- You're updating the schema across the entire platform
- You need to ensure all tenants are up to date
Output:
- Shows progress for each tenant
- Lists which migrations were applied
- Provides a summary at the end
- Exits with error code if any tenant fails
4. Rollback Migration (Manual)
npm run migrate:rollback
⚠️ Warning: This runs a rollback on the default database configured in knexfile.js. For tenant-specific rollbacks, you'll need to manually configure the connection.
Migration Flow
During New Tenant Provisioning
When a new tenant is created via the API, migrations are automatically run as part of the provisioning process:
- Tenant database is created
TenantProvisioningService.runTenantMigrations()is called- All migrations in
migrations/tenant/are executed
For Existing Tenants
When you add a new migration file and need to apply it to existing tenants:
-
Create the migration:
npm run migrate:make add_new_feature -
Edit the generated migration file in
migrations/tenant/ -
Test on a single tenant first:
npm run migrate:tenant test-tenant -
If successful, apply to all tenants:
npm run migrate:all-tenants
Migration Directory Structure
backend/
├── migrations/
│ └── tenant/ # Tenant-specific migrations
│ ├── 20250126000001_create_users_and_rbac.js
│ ├── 20250126000002_create_object_definitions.js
│ └── ...
├── scripts/
│ ├── migrate-tenant.ts # Single tenant migration
│ └── migrate-all-tenants.ts # All tenants migration
└── knexfile.js # Knex configuration
Security Notes
Database Password Encryption
Tenant database passwords are encrypted in the central database using AES-256-CBC encryption. The migration scripts automatically:
- Fetch tenant connection details from the central database
- Decrypt the database password using the
DB_ENCRYPTION_KEYenvironment variable - Connect to the tenant database
- Run migrations
- Close the connection
Required Environment Variable:
DB_ENCRYPTION_KEY=your-32-character-secret-key!!
This key must match the key used by TenantService for encryption.
Troubleshooting
Migration Fails for One Tenant
If migrate:all-tenants fails for a specific tenant:
- Check the error message in the output
- Investigate the tenant's database directly
- Fix the issue (manual SQL, data cleanup, etc.)
- Re-run migrations for that tenant:
npm run migrate:tenant <slug> - Once fixed, run
migrate:all-tenantsagain to ensure others are updated
Migration Already Exists
Knex tracks which migrations have been run in the knex_migrations table in each tenant database. If a migration was already applied, it will be skipped automatically.
Connection Issues
If you see connection errors:
- Verify the central database is accessible
- Check that tenant database credentials are correct
- Ensure
DB_ENCRYPTION_KEYmatches the one used for encryption - Verify the tenant's database server is running and accessible
Example Migration File
// migrations/tenant/20250126000006_add_custom_fields.js
exports.up = async function(knex) {
await knex.schema.table('field_definitions', (table) => {
table.boolean('is_custom').defaultTo(false);
table.string('custom_type', 50).nullable();
});
};
exports.down = async function(knex) {
await knex.schema.table('field_definitions', (table) => {
table.dropColumn('is_custom');
table.dropColumn('custom_type');
});
};
Best Practices
- Always test on a single tenant first before running migrations on all tenants
- Include rollback logic in your
down()function - Use transactions for complex multi-step migrations
- Backup production databases before running migrations
- Monitor the output when running
migrate:all-tenantsto catch any failures - Version control your migration files
- Document breaking changes in migration comments
- Consider data migrations separately from schema migrations when dealing with large datasets
CI/CD Integration
In your deployment pipeline, you can automatically migrate all tenants:
# After deploying new code
npm run migrate:all-tenants
Or integrate it into your Docker deployment:
# In your Dockerfile or docker-compose.yml
CMD npm run migrate:all-tenants && npm run start:prod