Au-delà du tutoriel basique, les migrations Drizzle en production demandent des patterns avancés : zero-downtime, rollback safe, gestion d’environnements multiples, CI/CD intégré. Voici le guide pratique 2026.
Ce guide général couvre les patterns avancés. Les articles connexes détaillent : zero-downtime migrations, Drizzle Studio en pratique, Drizzle vs Prisma, multi-tenancy avec Drizzle.
Workflow migrations en équipe
- Dev modifie schéma TS dans
src/db/schema.ts bun drizzle-kit generate→ fichier SQL versionné dansdrizzle/- Commit Git du fichier SQL ET du schéma TS
- Code review : revoir le SQL généré (drizzle-kit peut parfois générer des migrations destructives)
- Merge → CI lance
bun drizzle-kit migrateen staging - Validation staging → promote vers prod
- Déploiement prod : migrate puis nouvelle version app
Configuration multi-environnement
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
verbose: true,
strict: true,
schemaFilter: ["public"],
migrations: {
schema: "public",
table: "drizzle_migrations",
},
});
Migration zero-downtime
Voir notre tutoriel zero-downtime. Pattern : ajouter colonne nullable d’abord → backfill → contraintes plus tard.
CI/CD avec Forgejo Actions
# .forgejo/workflows/migrate.yml
name: Migrate DB
on:
push:
branches: [main]
paths:
- 'drizzle/**'
- 'src/db/schema.ts'
jobs:
migrate-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun drizzle-kit migrate
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
migrate-prod:
needs: migrate-staging
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun drizzle-kit migrate
env:
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
Drizzle Studio
UI graphique pour explorer la base. Voir notre tutoriel Drizzle Studio.
Multi-tenancy
Voir notre tutoriel multi-tenancy : approches schema-per-tenant ou row-level security.
Adaptation Afrique de l’Ouest
Pour PME africaines avec déploiements continus, le combo Drizzle + Forgejo Actions + Coolify donne un workflow CI/CD complet sans dépendance cloud externe. Migrations versionnées dans Git, déployées automatiquement après tests.
Dans la continuité
Drizzle ORM en 2026 : pourquoi le choisir pour vos migrations
Drizzle ORM s’est imposé en 2024 et 2025 comme l’alternative crédible à Prisma pour les projets TypeScript modernes. Là où Prisma ajoute un binaire Rust de 30 Mo et un schema DSL propriétaire, Drizzle reste un simple package Node.js qui parle SQL natif. Pour une équipe sénégalaise ou ivoirienne qui héberge sur un VPS Hetzner CX22 à 4,51 EUR (2 960 FCFA) avec 4 Go de RAM, l’économie de mémoire et le démarrage plus rapide font la différence. Drizzle Kit, l’outil CLI de migrations, est à la version 0.31+ en 2026 et gère PostgreSQL, MySQL, SQLite, ainsi que les variantes serverless (Neon, Turso, PlanetScale, Supabase).
Avant d’aborder ce tutoriel, vous devez avoir un projet Node.js 22 LTS ou plus récent, TypeScript 5.5+ et une base de données accessible (PostgreSQL 17 recommandé). Les exemples utilisent Postgres mais s’adaptent en changeant le driver. Le code est compatible avec Bun 1.2 si vous voulez un runtime plus rapide.
Étape 1 — Installer Drizzle et configurer drizzle.config.ts
npm install drizzle-orm postgres
npm install -D drizzle-kit @types/node
mkdir -p src/db
Créez ensuite drizzle.config.ts à la racine. Ce fichier dirige toute la chaîne de migration :
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
strict: true,
verbose: true,
} satisfies Config;
Le mode strict: true exige une confirmation avant chaque opération destructrice (drop column, drop table). C’est votre première ligne de défense contre les migrations malheureuses en production.
Étape 2 — Définir un schema typé
Le schema Drizzle est du TypeScript pur, donc l’autocomplétion fonctionne dans VS Code et les erreurs de typage sortent à la compilation, pas en runtime à 23 h un samedi.
// src/db/schema.ts
import { pgTable, serial, text, timestamp, integer, index } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
fullName: text("full_name").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
emailIdx: index("users_email_idx").on(table.email),
}));
export const orders = pgTable("orders", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
amountFcfa: integer("amount_fcfa").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
Notez le montant en FCFA stocké comme entier — règle d’or pour toute application financière en Afrique de l’Ouest : ne jamais utiliser numeric ou decimal pour des centimes virtuels qui n’existent pas dans la zone CFA. L’integer évite tous les bugs d’arrondi.
Étape 3 — Générer la première migration
npx drizzle-kit generate
Drizzle Kit compare le schema TypeScript avec l’état précédent (vide pour la première fois) et écrit un fichier SQL horodaté dans ./drizzle :
drizzle/
0000_burly_falcon.sql
meta/
_journal.json
0000_snapshot.json
Ouvrez le SQL généré et lisez-le entièrement. Drizzle est explicite : il génère du SQL standard sans magie. Si vous voyez un DROP TABLE inattendu, c’est qu’il a interprété un changement comme une suppression — ne jamais appliquer aveuglément en production.
Étape 4 — Appliquer la migration via drizzle-kit migrate
Trois méthodes existent. La plus sûre pour la production :
npx drizzle-kit migrate
Drizzle Kit applique les migrations dans l’ordre, marque chaque fichier exécuté dans la table drizzle.__drizzle_migrations (créée automatiquement) et empêche toute ré-exécution. C’est l’équivalent de Flyway ou Liquibase, mais 5 fois plus léger.
Pour un workflow programmatique (utile dans un container qui lance les migrations au démarrage) :
// scripts/migrate.ts
import { drizzle } from "drizzle-orm/postgres-js";
import { migrate } from "drizzle-orm/postgres-js/migrator";
import postgres from "postgres";
const sql = postgres(process.env.DATABASE_URL!, { max: 1 });
const db = drizzle(sql);
await migrate(db, { migrationsFolder: "./drizzle" });
await sql.end();
console.log("Migrations appliquees");
Le { max: 1 } est crucial : on n’utilise qu’une seule connexion pour les migrations, pour éviter les locks parallèles sur la table de versioning.
Étape 5 — Migrations zero-downtime sur PostgreSQL
Le plus dur n’est pas d’écrire une migration, c’est de la déployer sans coupure. Trois patterns standards à connaître par cœur.
Renommer une colonne ne se fait jamais en une seule étape. Procédez en 3 déploiements : (1) ajouter la nouvelle colonne et écrire dans les deux ; (2) backfiller les données et lire depuis la nouvelle ; (3) supprimer l’ancienne. Drizzle ne masque pas cette complexité — vous écrivez les 3 schemas successifs, donc vous mesurez l’impact.
Ajouter une colonne NOT NULL sur une grosse table (10 millions de lignes) avec une DEFAULT non triviale lock la table le temps de la réécriture. Sur Postgres 11+, ajouter une colonne avec une valeur par défaut constante est instantané (métadonnée), mais une valeur par défaut now() ou uuid_generate_v4() déclenche une réécriture longue. Solution : ajouter en nullable, backfiller en batches, puis ajouter la contrainte.
-- drizzle/0003_add_phone.sql (manuel pour zero-downtime)
ALTER TABLE users ADD COLUMN phone text;
-- Deploiement 1 : code lit/ecrit phone (nullable)
-- Backfill batch dans un script :
-- UPDATE users SET phone = '+221xxxxxxxxx' WHERE id BETWEEN 1 AND 10000;
-- Deploiement 2 : ajouter la contrainte
ALTER TABLE users ALTER COLUMN phone SET NOT NULL;
Index concurrent : la directive CREATE INDEX CONCURRENTLY permet de créer un index sans bloquer les lectures/écritures. Drizzle Kit ne le génère pas par défaut — éditez le SQL à la main pour les grosses tables.
Étape 6 — Pipeline CI/CD avec GitHub Actions
Pour une PME ivoirienne qui déploie via GitHub Actions vers un VPS Hetzner, le workflow type :
# .github/workflows/migrate.yml
name: Database Migration
on:
push:
branches: [main]
paths: ["drizzle/**", "src/db/schema.ts"]
jobs:
migrate:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Lint migration safety
run: npx drizzle-kit check
- name: Apply migrations
env:
DATABASE_URL: ${{ secrets.DATABASE_URL_PROD }}
run: npx drizzle-kit migrate
La commande drizzle-kit check détecte les incohérences entre le schema, les snapshots et les fichiers SQL. Si quelqu’un a édité un SQL sans regénérer le snapshot, le job échoue et bloque le merge — protection contre les drifts silencieux.
Étape 7 — Rollback : la stratégie qui marche vraiment
Drizzle Kit ne génère pas de scripts down. C’est un choix philosophique : un rollback automatique est rarement sûr en production (perte de données). La doctrine moderne est roll forward : créez une nouvelle migration qui annule les effets indésirables. Cela force une réflexion sur l’impact (quoi faire des lignes insérées dans la nouvelle colonne ?).
Pour les vraies catastrophes, comptez sur votre backup Restic des volumes Postgres avec une RPO de 24 h et un test de restauration mensuel. C’est le filet de sécurité ultime.
Étape 8 — Drizzle Studio pour explorer la base
npx drizzle-kit studio
Le studio démarre une UI web sur https://local.drizzle.studio qui affiche le schema, permet de requêter et de modifier des lignes. Pratique pour des opérations one-shot en staging. Ne jamais l’exposer en production — limitez à votre poste local via le port 4983.
Pour étoffer le tableau
Cette série couvre 95 % des besoins en migrations. Pour gérer la concurrence des déploiements en équipe, combinez avec un Docker Compose en production qui lance les migrations dans un init-container, et une supervision active via Uptime Kuma. Pour des bases multi-tenants ou des pipelines event-sourcing, regardez Drizzle Relations API et l’extension drizzle-zod qui génère les schemas Zod depuis votre table.
Patterns avancés : transactions, seeds et données de test
Drizzle expose une API db.transaction() idiomatique en TypeScript. Toute migration de données complexe (par exemple : déplacer un champ d’une table à une autre en agrégeant) doit s’exécuter dans une transaction pour garantir l’atomicité. Si une étape échoue à mi-parcours, Postgres rollback automatiquement.
// scripts/backfill-orders.ts
await db.transaction(async (tx) => {
const oldOrders = await tx.select().from(legacyOrders);
for (const o of oldOrders) {
await tx.insert(orders).values({
userId: o.user_id,
amountFcfa: Math.round(o.amount_xof),
createdAt: o.created_at,
});
}
await tx.execute(sql`TRUNCATE TABLE legacy_orders`);
});
Pour les seeds (jeux de données initiales : régions du Sénégal, communes d’Abidjan, codes opérateurs Orange/MTN), créez un fichier scripts/seed.ts qui s’exécute après les migrations en environnement non-production. Drizzle Kit 0.31+ inclut une commande drizzle-kit seed avec génération de données factices basées sur Faker.
Branching de schemas avec Neon ou Turso
Si votre base est sur Neon (Postgres serverless) ou Turso (libSQL serverless), profitez du branching : chaque feature branch GitHub crée automatiquement une branche de base de données isolée. Le workflow Drizzle reste identique, vous changez juste le DATABASE_URL par environnement. Coût Neon : gratuit jusqu’à 0,5 Go, puis 19 USD par mois (12 460 FCFA) pour 10 Go — pertinent pour des prototypes mais l’auto-hébergé Hetzner reste plus économique en production.
Sur Turso, chaque base est répliquée en edge (Paris, Frankfurt, Bahrain, Mumbai). Pour une app avec utilisateurs entre Dakar, Conakry et Abidjan, la latence tombe à 8-15 ms contre 60-90 ms pour un Postgres classique en Allemagne. Drizzle s’y connecte via drizzle-orm/libsql.
Diagnostic : que faire quand drizzle-kit refuse de générer
Trois cas fréquents et leur résolution. Premièrement, « No schema changes detected » alors que vous avez modifié schema.ts : vérifiez que drizzle.config.ts pointe bien vers le bon fichier et que le snapshot précédent dans drizzle/meta n’est pas corrompu. Deuxièmement, « Cannot drop column without confirmation » en mode strict : ajoutez le flag --breakpoints et lisez attentivement avant de valider. Troisièmement, « relation already exists » au déploiement : la table __drizzle_migrations est désynchronisée — comparez les hashs entre drizzle/meta/_journal.json et la table en base, puis corrigez manuellement la ligne fautive.
Bilan et stack recommandée pour 2026
Pour une équipe de 2 à 10 développeurs en Afrique de l’Ouest francophone, la stack qui équilibre productivité et coûts : Node.js 22 LTS + TypeScript 5.6 + Drizzle ORM 0.36+ + Drizzle Kit 0.31+ + PostgreSQL 17 sur Hetzner ou Scaleway Paris, le tout supervisé par Uptime Kuma et sauvegardé via Restic. Comptez 30 à 60 minutes pour câbler la première migration sur un projet existant, et 5 à 10 minutes par migration suivante. C’est la productivité que Prisma promettait sans le poids du runtime ni les surprises sur les déploiements lourds.
Comparatif rapide Drizzle vs Prisma vs Kysely
Trois ORM et query builders TypeScript dominent 2026. Drizzle gagne sur la légèreté (0 binaire, démarrage à froid de 50 ms contre 800 ms pour Prisma sur un VPS modeste) et sur la transparence du SQL généré. Prisma reste plus mature pour les très gros schemas (200+ tables) grâce à son introspection et son éditeur visuel Prisma Studio. Kysely est un pur query builder sans gestion de migrations, intéressant si vous gérez la couche schema avec un autre outil comme Atlas ou Sqitch. Pour une PME ouest-africaine qui cherche un compromis productivité/ressources/contrôle, Drizzle reste le meilleur choix par défaut en 2026, avec une courbe d’apprentissage de 2 à 3 jours pour un développeur Node.js confirmé.
Ressources officielles utiles à garder sous la main
La documentation Drizzle ORM officielle évolue rapidement : consultez orm.drizzle.team pour les notes de version, et le dépôt GitHub drizzle-team/drizzle-orm pour les exemples par dialecte. Le canal Discord communautaire répond généralement en moins d’une heure aux questions techniques pointues, ce qui rassure quand on déploie une migration critique un dimanche soir depuis Dakar ou Abidjan, fuseau horaire compatible avec l’équipe core européenne.