# 09. Database — Migrate Full Prisma Schema to Drizzle ORM meta: id: shieldai-unified-restructure-09 feature: shieldai-unified-restructure priority: P0 depends_on: [] tags: [backend, database, drizzle, schema] objective: - Convert the entire 900-line Prisma schema from `packages/db/prisma/schema.prisma` into Drizzle ORM TypeScript schema files in `web/src/server/db/schema.ts` and related files. This is the foundational data layer for the unified monolith. deliverables: - `web/src/server/db/schema.ts` — Main schema file with all tables: - User & Authentication (User, Account, Session, DeviceToken) - Family & Subscription (FamilyGroup, FamilyGroupMember, Subscription) - DarkWatch (WatchlistItem, Exposure) - Notifications & Alerts (Alert) - VoicePrint (VoiceEnrollment, VoiceAnalysis, AnalysisJob, AnalysisResult) - SpamShield (SpamFeedback, SpamRule) - Audit & Analytics (AuditLog, KPISnapshot) - Correlation (NormalizedAlert, CorrelationGroup) - Reports (SecurityReport) - Marketing (WaitlistEntry, BlogPost) - HomeTitle (PropertyWatchlistItem, PropertySnapshot, PropertyChange) - RemoveBrokers (InfoBroker, RemovalRequest, BrokerListing) - `web/src/server/db/schema/` — Optional split directory if single file becomes unwieldy: - `auth.ts`, `subscription.ts`, `darkwatch.ts`, `voiceprint.ts`, `spamshield.ts`, `alerts.ts`, `correlation.ts`, `reports.ts`, `marketing.ts`, `hometitle.ts`, `removebrokers.ts` - All enums defined as TypeScript const arrays or Drizzle `pgEnum` - All indexes, unique constraints, and foreign keys preserved - Relations defined using Drizzle's `relations()` helper steps: 1. Read and analyze `packages/db/prisma/schema.prisma` completely. Document every model, enum, relation, index, and constraint. 2. Install Drizzle ORM and PostgreSQL driver in `web/`: - `drizzle-orm` - `pg` (node-postgres) or `@neondatabase/serverless` if using Neon - `drizzle-kit` for migrations 3. Create `web/src/server/db/schema.ts` (or split directory). 4. For each Prisma model, create a Drizzle table definition: - Map Prisma field types to Drizzle column types: - `String` → `varchar`, `text`, `uuid` - `Int` → `integer` - `Float` → `real` - `Boolean` → `boolean` - `DateTime` → `timestamp` - `Json` → `jsonb` - `String[]` → `text().array()` - Preserve `@id`, `@default(uuid())`, `@unique`, `@index`, `@relation` - Map Prisma enums to Drizzle `pgEnum()` 5. Define all indexes using Drizzle's `.index()` and `.unique()` on table definitions. 6. Define relations using `relations()` helper for: - User → accounts, sessions, familyGroups, subscriptions, alerts, voice enrollments, etc. - Subscription → watchlistItems, exposures, alerts, propertyWatchlistItems, removalRequests - WatchlistItem → exposures - PropertyWatchlistItem → snapshots, changes - And all other one-to-many / many-to-one relations 7. Export a unified schema object for use with Drizzle Kit. 8. Verify the schema compiles without TypeScript errors. 9. Generate an ER diagram or schema summary for documentation. steps: - Unit: Schema file compiles without TS errors - Integration: `drizzle-kit generate` produces migration SQL that matches Prisma schema structure - Compare: Automated or manual comparison of Prisma schema vs Drizzle schema to ensure no models/fields are missing acceptance_criteria: - [ ] Every model from Prisma schema has a corresponding Drizzle table definition - [ ] All enums are defined and used correctly - [ ] All primary keys, unique constraints, and indexes are preserved - [ ] All foreign key relations are defined using Drizzle relations - [ ] `drizzle-kit generate` runs successfully and outputs SQL - [ ] Generated SQL creates tables with correct column types and constraints - [ ] No data loss: the new schema is structurally equivalent to the old one validation: - Run `cd web && npx drizzle-kit generate` and inspect generated SQL - Compare table count: Prisma schema has X models, Drizzle schema has X tables - Verify enum values match exactly between Prisma and Drizzle - Run `npx drizzle-kit push` against a local PostgreSQL instance and confirm all tables are created notes: - This is the most critical backend task. A missing field or incorrect relation will cascade into broken tRPC routers. - Prisma's implicit many-to-many relations must be explicitly defined as junction tables in Drizzle. - Prisma's `@updatedAt` auto-timestamp can be replicated with Drizzle's `$onUpdateFn(() => new Date())`. - Keep the old Prisma schema file as reference until task 41. - Consider using `drizzle-zod` later (task 11+) to auto-generate validation schemas from Drizzle tables. - The schema uses PostgreSQL-specific features (arrays, enums, jsonb). Ensure the Drizzle definitions use `pgTable`, `pgEnum`, etc.