# Database Models Changemaker Lite V2 uses a comprehensive PostgreSQL database schema with 30+ models across authentication, campaigns, locations, media, and content management. The schema is managed via Prisma ORM (main API) and Drizzle ORM (media API). ## Model Organization Models are organized by feature area: ### [Authentication & Users](auth.md) Core authentication and user management: - **User** - User accounts with roles and authentication - **RefreshToken** - JWT refresh token tracking - **Session** - User session management (future) ### [Influence Module](influence.md) Advocacy campaign models: - **Campaign** - Campaign definitions and settings - **CampaignEmail** - Sent email tracking - **Response** - Public response wall submissions - **PostalCodeCache** - Representative lookup cache ### [Map Module](map.md) Location and geographic models: - **Location** - Address database with geocoding - **Cut** - Geographic polygon organization - **Shift** - Volunteer shift scheduling - **MapSettings** - Map configuration singleton ### [Canvassing](canvass.md) Door-to-door canvassing models: - **CanvassSession** - Canvassing session tracking - **CanvassVisit** - Visit outcome recording - **TrackingSession** - GPS tracking (future) ### [Content Management](pages.md) Landing pages and content: - **Page** - Landing page definitions - **PageBlock** - Reusable content blocks ### [Email Templates](email-templates.md) Email template system: - **EmailTemplate** - Template definitions - **EmailTemplateVersion** - Version history (future) ### [Media](media.md) Video library (Drizzle ORM): - **videos** - Video metadata and files - **shared_media** - Public gallery assignments - **media_reactions** - Emoji reactions - **media_jobs** - Background job queue ### [Settings](settings.md) Global configuration: - **Settings** - Site-wide settings singleton ## ORM Architecture ### Prisma (Main API) Used for 95% of models: - **Schema:** `api/prisma/schema.prisma` - **Migrations:** `api/prisma/migrations/` - **Client:** Auto-generated TypeScript types - **Database:** PostgreSQL 16 ### Drizzle (Media API) Used for media models only: - **Schema:** `api/src/modules/media/db/schema.ts` - **Migrations:** None (push-based) - **Client:** Manual schema definition - **Database:** Same PostgreSQL 16 ## Common Patterns ### Timestamps Most models include: ```prisma createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ``` ### Foreign Keys Relations use explicit foreign key fields: ```prisma model Campaign { id Int @id @default(autoincrement()) createdByUserId Int createdBy User @relation(fields: [createdByUserId], references: [id]) } ``` ### JSON Fields Flexible data stored as JSON: ```prisma model Campaign { emailTemplate Json? settings Json? } ``` TypeScript types: ```typescript import { Prisma } from '@prisma/client'; const template: Prisma.InputJsonValue = { subject: 'Email subject', body: 'Email body', }; ``` ### Enums Type-safe enumerations: ```prisma enum Role { SUPER_ADMIN INFLUENCE_ADMIN MAP_ADMIN USER TEMP } enum VisitOutcome { SUCCESS NOT_HOME MOVED REFUSED WRONG_ADDRESS INACCESSIBLE OTHER } ``` ## Model Count by Category | Category | Models | ORM | |----------|--------|-----| | Authentication | 3 | Prisma | | Influence | 4 | Prisma | | Map | 4 | Prisma | | Canvassing | 3 | Prisma | | Content | 2 | Prisma | | Email Templates | 2 | Prisma | | Media | 4 | Drizzle | | Settings | 2 | Prisma | | **Total** | **24** | Mixed | ## Database Operations ### Migrations (Prisma) ```bash # Create migration cd api && npx prisma migrate dev --name add_field # Deploy migrations cd api && npx prisma migrate deploy # Reset database (dev only) cd api && npx prisma migrate reset ``` ### Schema Push (Drizzle) ```bash # Push schema changes (media API) cd api && npx drizzle-kit push ``` ### Database Browser View data via: - **Prisma Studio:** `npx prisma studio` - **NocoDB:** http://localhost:8091 (read-only) ## Indexes Key indexes for performance: ```prisma model Location { @@index([cutId]) @@index([lastVisitedAt]) } model Campaign { @@index([published]) @@index([createdByUserId]) } model CanvassSession { @@index([userId]) @@index([status]) } ``` ## Constraints ### Unique Constraints ```prisma model User { email String @unique } model Page { slug String @unique } model Cut { name String @unique } ``` ### Check Constraints Enforced at application level: - Email format validation - Password complexity (12+ chars) - Coordinate bounds (-90 to 90 lat, -180 to 180 lng) ## Relations ### One-to-Many ```prisma model User { id Int @id @default(autoincrement()) campaigns Campaign[] } model Campaign { id Int @id @default(autoincrement()) createdByUserId Int createdBy User @relation(fields: [createdByUserId], references: [id]) } ``` ### Many-to-Many Via junction tables: ```prisma model Shift { id Int @id @default(autoincrement()) signups ShiftSignup[] } model User { id Int @id @default(autoincrement()) signups ShiftSignup[] } model ShiftSignup { id Int @id @default(autoincrement()) shiftId Int userId Int shift Shift @relation(fields: [shiftId], references: [id]) user User @relation(fields: [userId], references: [id]) @@unique([shiftId, userId]) } ``` ## Seeding Initial data in `api/prisma/seed.ts`: - Admin user (admin@example.com) - Default settings - Sample page blocks - System email templates ```bash # Run seed cd api && npx prisma db seed ``` ## Data Types ### Common Types - **ID:** `Int @id @default(autoincrement())` - **String:** `String` or `String @db.Text` (long text) - **Number:** `Int` or `Float` - **Boolean:** `Boolean @default(false)` - **Date:** `DateTime @default(now())` - **JSON:** `Json` or `Json?` - **Enum:** `Role`, `VisitOutcome`, etc. ### Spatial Data GeoJSON stored as JSON: ```prisma model Cut { geometry Json // GeoJSON Polygon } ``` Coordinates as separate fields: ```prisma model Location { latitude Float longitude Float } ``` ## Database Configuration ### Connection String ```bash DATABASE_URL="postgresql://user:password@localhost:5432/changemaker_v2?schema=public" ``` ### Connection Pool Prisma connection pool: ```typescript // api/src/server.ts const prisma = new PrismaClient({ log: ['error', 'warn'], }); ``` ## Related Documentation - [Authentication Models](auth.md) - [Influence Models](influence.md) - [Map Models](map.md) - [Canvassing Models](canvass.md) - [Content Models](pages.md) - [Email Template Models](email-templates.md) - [Media Models](media.md) - [Settings Models](settings.md) - [Database Overview](../index.md) - [Migrations Guide](../../development/migrations.md) - [Backend Modules](../../backend/modules/index.md)