How to generate and run database migrations with Drizzle ORM
Concordia uses Drizzle ORM for type-safe database migrations. Migrations are automatically generated from schema changes and can be applied to development or production databases.
# Local development databaseDATABASE_URL_LOCAL=postgresql://user:password@localhost:5432/concordia_dev# Production databaseDATABASE_URL_PROD=postgresql://user:password@host:5432/concordia_prod# Use production database (default: false)USE_PROD_DB=false# Required for production operationsCONFIRM_PROD=oui
If you add new schema files, the generator will prompt you:
[INTERACTIF] Certains fichiers ne sont pas importés dans schemas.ts : [1] services_bookings.ts [2] notification.tsVoulez-vous ajouter des imports à schemas.ts ? (ex: 1,2 ou rien pour ignorer) :
Enter the numbers of schemas to import, separated by commas.
═══════════════════════════════════════════════════════ 🚀 Migration Drizzle - Connexion local/dev 🚀═══════════════════════════════════════════════════════Cible DB: postgresql://***@localhost:5432/concordia_dev (local/dev)Connexion au client PostgreSQL...[migrate] → 0005_harsh_metal_master✔️ Toutes les migrations ont été appliquées.═══════════════════════════════════════════════════════
✔️ Aucun changement — aucune migration en attente pour la base concordia_dev (local/dev).✔️ Aucune migration à appliquer.Toutes les migrations sont déjà appliquées.
# Generate migrations firstnpm run db:generate# Review the generated SQL filescat src/database/migrations/0006_*.sql# Then apply themnpm run db:migrate
Always test migrations in development before production:
# 1. Generate migrationnpm run db:generate# 2. Test locallynpm run db:migrate# 3. Verify application worksnpm run dev# 4. Deploy to productionUSE_PROD_DB=true CONFIRM_PROD=oui npm run db:migrate
Safe - Add new nullable columns or columns with defaults:
// Safe: nullable columnexport const user = pgTable("user", { // ... existing fields bio: text("bio"), // nullable by default});// Safe: column with defaultexport const post = pgTable("post", { // ... existing fields views: integer("views").default(0).notNull(),});
Be careful - Ensure no code references the column:
// Before removing, search codebase for references// Then remove from schemaexport const user = pgTable("user", { id: text("id").primaryKey(), name: text("name").notNull(), // oldField: text("old_field"), // REMOVED});
Requires data migration:
// 1. Add new columnexport const user = pgTable("user", { // ... existing fullName: text("full_name"), // new});// 2. Generate migration// 3. Manually add data copy to migration:// UPDATE user SET full_name = name;// 4. Update code to use new column// 5. Generate another migration to drop old column
Check existing data first:
// Before adding NOT NULL constraint:// SELECT COUNT(*) FROM user WHERE email IS NULL;export const user = pgTable("user", { id: text("id").primaryKey(), email: text("email").notNull(), // Adding NOT NULL});
For complex migrations involving data transformation:
1
Generate schema migration
npm run db:generate
2
Edit SQL file
Open the generated migration file and add data transformation:
-- Generated by DrizzleALTER TABLE "user" ADD COLUMN "full_name" text;-- Manual data migrationUPDATE "user" SET "full_name" = "name" WHERE "full_name" IS NULL;-- Generated by DrizzleALTER TABLE "user" DROP COLUMN "name";