5.1 KiB
5.1 KiB
10. Database — PostgreSQL Connection, Migrations, and Seed Data
meta: id: kordant-unified-restructure-10 feature: kordant-unified-restructure priority: P0 depends_on: [kordant-unified-restructure-09] tags: [backend, database, drizzle, postgres]
objective:
- Set up the database connection layer, migration tooling, and seed data for the unified monolith. Replace the current SQLite setup in
web/with PostgreSQL, matching the production database used by the legacypackages/db.
deliverables:
web/src/server/db/index.ts— Database connection module:- Creates PostgreSQL connection pool using
pgor@neondatabase/serverless - Exports
dbinstance initialized with Drizzle ORM - Handles connection string from environment variables
- Graceful shutdown hook to close pool
- Creates PostgreSQL connection pool using
web/drizzle.config.ts— Drizzle Kit configuration:- Points to
src/server/db/schema.ts - Specifies PostgreSQL dialect
- Reads database URL from env
- Points to
web/src/server/db/migrate.ts— Migration runner script:- Programmatically runs pending migrations on startup
- Can be called from
entry-server.tsxor a standalone script
web/src/server/db/seed.ts— Seed script:- Creates sample users, subscriptions, watchlist items, alerts, blog posts
- Idempotent: can be run multiple times without duplicates
- Useful for development and demo environments
- Environment configuration:
DATABASE_URLin.envand.env.example- Connection pooling settings (max connections, timeout)
steps:
- Install dependencies in
web/:drizzle-orm,drizzle-kitpg(for local/dev) or@neondatabase/serverless(for serverless deploy)@types/pgif usingpg
- Create
web/src/server/db/index.ts:import { drizzle } from 'drizzle-orm/node-postgres'; import { Pool } from 'pg'; import * as schema from './schema'; const pool = new Pool({ connectionString: process.env.DATABASE_URL }); export const db = drizzle(pool, { schema }); - Update
web/drizzle.config.ts:import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/server/db/schema.ts', out: './drizzle', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL! }, }); - Create
web/src/server/db/migrate.ts:- Import
migratefromdrizzle-orm/node-postgres/migrator - Run migrations from
./drizzlefolder - Log success or error
- Import
- Create
web/src/server/db/seed.ts:- Use
db.insert()to populate tables - Create: 2-3 users, 1 family group, 2 subscriptions (basic + premium), 3-5 watchlist items, 2-3 exposures, 5-10 alerts, 3 blog posts, 2 property watchlist items, 1 removal request
- Use deterministic IDs or check for existing data before inserting
- Use
- Add
db:migrate,db:generate,db:push,db:seedscripts toweb/package.json. - Create
.env.exampleinweb/withDATABASE_URL=postgresql://... - Test locally:
- Start PostgreSQL (Docker or local install)
- Run
pnpm db:generateto create migration SQL - Run
pnpm db:pushto apply schema - Run
pnpm db:seedto populate data - Verify tables exist with correct data using
psqlor a GUI tool
- Update
web/src/server/db/schema.tsfrom task 09 if any adjustments are needed based on migration output.
steps:
- Integration:
pnpm db:generateproduces valid SQL - Integration:
pnpm db:pushcreates all tables in local PostgreSQL - Integration:
pnpm db:seedpopulates tables without errors - Integration: Application can query seeded data via
db.select()
acceptance_criteria:
web/src/server/db/index.tsexports a working Drizzledbinstancedrizzle.config.tsis correctly configured for PostgreSQLpnpm db:generatecreates migration files inweb/drizzle/pnpm db:pushapplies schema to a PostgreSQL database successfullypnpm db:seedpopulates all relevant tables with sample data- The app can perform
db.select().from(users)and return seeded users - Environment variables are documented in
.env.example
validation:
cd web && pnpm db:generate— checkdrizzle/folder for SQL filescd web && pnpm db:push— verify tables created viapsql -d kordant -c "\dt"cd web && pnpm db:seed— verify data exists viapsql -d kordant -c "SELECT COUNT(*) FROM users;"- Create a temporary test route that queries
db.select().from(users)and renders results
notes:
- The legacy project used Prisma + PostgreSQL in production. We are keeping PostgreSQL but switching to Drizzle ORM.
- For local development, Docker Compose with PostgreSQL is recommended:
services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: kordant POSTGRES_PASSWORD: kordant POSTGRES_DB: kordant ports: - "5432:5432" - If deploying to Vercel/Netlify serverless, use
@neondatabase/serverlessinstead ofpg. - Migration files should be committed to git so all environments run the same migrations.
- The seed script should be idempotent. Use
ON CONFLICT DO NOTHINGor check existence before inserting.