# Drizzle-Migrationen — Bootstrap und Workflow Stand: 2026-05-12. Cards hat bis 2026-05-12 alle Schema-Änderungen über `drizzle-kit push --force` gefahren — Schema-Sync ohne versionierte Migrationen. Live ist das gefährlich: kein Audit-Trail, keine sichere Rollback-Story, schwer reviewable. Ab Commit `` gibt es eine **versionierte Migration-Welt** mit einer Baseline (`0000_baseline.sql`), die das bestehende Schema festfriert. --- ## Was lokal schon passiert ist 1. `pnpm drizzle:generate` → `apps/api/src/db/migrations/0000_baseline.sql` (25 Tabellen, 5 Enums, alle FKs/Indizes — das gesamte cards- und marketplace-Schema, eingefroren auf den Stand 2026-05-12). 2. `apps/api/scripts/bootstrap-drizzle-tracking.ts` (neues npm-script `pnpm drizzle:bootstrap-tracking`) — markiert in einer existierenden Live-DB die Baseline als „bereits angewandt", ohne SQL erneut auszuführen. 3. Lokal verifiziert: Bootstrap ist idempotent, `pnpm drizzle:migrate` erkennt die Migration danach als bekannt. --- ## Was du auf der Prod-Box machen musst (einmalig) ```bash ssh mana-server cd ~/projects/cards git pull --ff-only origin main ``` ### 1. Bootstrap auf der Live-DB ausführen ```bash # DATABASE_URL aus der env-Datei lesen, nicht hart codieren. DB_PW=$(grep CARDS_DB_PASSWORD infrastructure/.env.production | cut -d= -f2-) docker exec -e DATABASE_URL="postgresql://cards:${DB_PW}@cards-postgres:5432/cards" \ cards-api node /app/apps/api/scripts/bootstrap-drizzle-tracking.ts ``` …**oder** wenn das im Container-Image-Setup nicht klappt (Script ist TypeScript, der API-Container kommt mit Bun): ```bash # Alternative: vom Host aus über Container-Network ausführen. # Mac Mini hat Bun installiert. cd ~/projects/cards/apps/api DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \ bun run scripts/bootstrap-drizzle-tracking.ts ``` **Erwartete Ausgabe:** ``` MARKED 0000_baseline (hash 312d67ba1aeb…) Tracking-Tabelle hat jetzt 1 Eintrag/Einträge. ``` Wenn statt `MARKED` schon `SKIP` kommt: jemand hat das Script bereits gelaufen lassen. Idempotent → kein Problem. ### 2. Verifizieren ```bash docker exec cards-postgres psql -U cards -d cards -c \ "SELECT * FROM drizzle.__drizzle_migrations;" ``` Erwartete Antwort: eine Zeile mit Hash `312d67ba1aeb…` und `created_at = 1778604624860`. ### 3. Smoke-Test ```bash cd ~/projects/cards/apps/api DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \ bun x drizzle-kit migrate ``` Erwartet: `[✓] migrations applied successfully!` ohne dass das Schema wirklich angefasst wird (es kommt nur ein NOTICE „relation '__drizzle_migrations' already exists, skipping"). --- ## Künftiger Workflow ### Neue Schema-Änderung 1. **Editiere das Schema** in `apps/api/src/db/schema/**.ts`. 2. **Generiere die Migration** lokal: ```bash cd apps/api pnpm drizzle:generate --name ``` 3. **Inspiziere** das generierte SQL in `src/db/migrations/0001_.sql` (oder höher) — bei destruktiven Änderungen (DROP COLUMN, ALTER TYPE) ggf. von Hand patchen, damit Daten nicht verloren gehen. 4. **Commit** Schema-Files + Migration-Files + Journal zusammen: ```bash git add apps/api/src/db/schema apps/api/src/db/migrations git commit -m "db(cards): " ``` 5. **Test lokal**: ```bash DATABASE_URL="postgresql://cards:cards@localhost:5435/cards" \ pnpm drizzle:migrate ``` ### Deploy 1. `git push` auf Forgejo. 2. Auf der Box: `git pull --ff-only`. 3. **Migration anwenden** (kein push --force mehr!): ```bash cd ~/projects/cards/apps/api DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \ bun x drizzle-kit migrate ``` 4. Erst danach Container rebuilden + restarten, wenn der neue Code auf das neue Schema angewiesen ist. --- ## Tabu - **`drizzle-kit push --force` auf Prod nie wieder.** Das Script bleibt in `package.json` als Dev-Convenience, aber für Prod ist nur `drizzle-kit migrate` der Weg. - **Migration-Files nicht nachträglich umschreiben**, sobald sie in einem Prod-Pull-Stand sind — der Hash würde sich ändern und Drizzle würde sie erneut anwenden wollen. - **Wenn du das Schema lokal mit `push` schon „weiter" als die Migration hast**, ist das ein Drift-Bug. Lösung: lokal die DB rückbauen (`drop schema cards cascade; drop schema marketplace cascade`), dann Migrationen frisch anwenden. Niemals diesen Drift in einen Commit hineinwachsen lassen. --- ## Bei Problemen - `pnpm drizzle:migrate` schmeißt „relation already exists" und bricht ab? Bootstrap-Script ist nie gelaufen — Schritt 1 oben. - Hash-Mismatch (Migration angeblich „neu" trotz Bootstrap)? Das Migration-File wurde nach dem Bootstrap noch ediert. Entweder Edit rückgängig oder den Tracking-Eintrag von Hand auf den neuen Hash bringen — ausschließlich nach Code-Review.