Base de Datos
Migrations
Schema changes are managed through Knex.js migrations in twin-backend/packages/database/src/migrations/.
Location
Code
Naming Convention
Code
YYYYMMDD-- date the migration was created######-- six-digit sequence number (usually000000,000001, etc.)description-- snake_case description of the change
Examples:
20260406000000_add_source_to_minting_transaction.ts20260407000001_partial_unique_ops_token.ts
Commands
Run from the database package directory:
Code
Rules
Use import type for Knex
Knex is a CommonJS module but migrations run with sucrase/ESM. Always use the type-only import:
Code
Idempotency
Migrations that insert data must be idempotent (safe to run multiple times). Use check-before-insert:
Code
For index creation, use IF NOT EXISTS:
Code
Batch Behavior
- All pending migrations run in a single batch
- If any migration in the batch fails, all migrations in that batch are rolled back
- Each migration's
upfunction runs in its own transaction by default
Partial Unique Indexes for Soft Delete
Tables with soft delete (deleted_at) must use partial unique indexes instead of regular unique constraints. Without this, soft-deleted rows occupy the index and block recreation.
Code
Tables using this pattern:
circulation_wallet--(ops_token_id, wallet_address) WHERE deleted_at IS NULLops_token--(LOWER(address), network) WHERE deleted_at IS NULLbridge_route--(bridge_token_key, network) WHERE deleted_at IS NULL
Snake Case
All column names use snake_case in migrations. Objection.js models handle the conversion to camelCase in JavaScript via knexSnakeCaseMappers.
Code
Last modified on