Files
Kordant/tasks/kordant-unified-restructure/10-db-connection-migrations.md
2026-05-28 08:59:24 -04:00

108 lines
5.2 KiB
Markdown

# 10. Database — Turso (SQLite) 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, turso, sqlite]
objective:
- Set up the database connection layer, migration tooling, and seed data for the unified monolith. Configure Turso (libsql/SQLite) as the database backend, matching the production database used by the legacy `packages/db`.
deliverables:
- `web/src/server/db/index.ts` — Database connection module:
- Creates Turso (libsql) client using `@libsql/client`
- Exports `db` instance initialized with Drizzle ORM
- Handles connection URL and auth token from environment variables
- Graceful shutdown hook to close client
- `web/drizzle.config.ts` — Drizzle Kit configuration:
- Points to `src/server/db/schema/index.ts`
- Specifies `turso` dialect
- Reads database URL and auth token from env
- `web/src/server/db/migrate.ts` — Migration runner script:
- Programmatically runs pending migrations on startup
- Can be called from `entry-server.tsx` or 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_URL` and `DATABASE_AUTH_TOKEN` in `.env` and `.env.example`
steps:
1. Install dependencies in `web/`:
- `drizzle-orm`, `drizzle-kit`
- `@libsql/client` (Turso/libsql client)
2. Create `web/src/server/db/index.ts`:
```ts
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import * as schema from './schema';
const client = createClient({
url: process.env.DATABASE_URL,
authToken: process.env.DATABASE_AUTH_TOKEN,
});
export const db = drizzle(client, { schema });
```
3. Update `web/drizzle.config.ts`:
```ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/server/db/schema/index.ts',
out: './drizzle',
dialect: 'turso',
dbCredentials: {
url: process.env.DATABASE_URL!,
authToken: process.env.DATABASE_AUTH_TOKEN,
},
});
```
4. Create `web/src/server/db/migrate.ts`:
- Import `migrate` from `drizzle-orm/libsql/migrator`
- Run migrations from `./drizzle` folder
- Log success or error
5. 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
6. Add `db:migrate`, `db:generate`, `db:push`, `db:seed` scripts to `web/package.json`.
7. Create `.env.example` in `web/` with `DATABASE_URL=libsql://...` and `DATABASE_AUTH_TOKEN`
8. Test locally:
- Create a Turso database (or use local SQLite with `libsql://./dev.db`)
- Run `pnpm db:generate` to create migration SQL
- Run `pnpm db:push` to apply schema
- Run `pnpm db:seed` to populate data
- Verify tables exist with correct data using `turso db shell` or a SQLite GUI tool
9. Update `web/src/server/db/schema.ts` from task 09 if any adjustments are needed based on migration output.
steps:
- Integration: `pnpm db:generate` produces valid SQL
- Integration: `pnpm db:push` creates all tables in local Turso/SQLite
- Integration: `pnpm db:seed` populates tables without errors
- Integration: Application can query seeded data via `db.select()`
acceptance_criteria:
- [ ] `web/src/server/db/index.ts` exports a working Drizzle `db` instance with Turso client
- [ ] `drizzle.config.ts` is correctly configured for Turso (dialect: 'turso')
- [ ] `pnpm db:generate` creates migration files in `web/drizzle/`
- [ ] `pnpm db:push` applies schema to a Turso database successfully
- [ ] `pnpm db:seed` populates 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` — check `drizzle/` folder for SQL files
- `cd web && pnpm db:push` — verify tables created via `turso db shell <database> "SELECT name FROM sqlite_master WHERE type='table';"`
- `cd web && pnpm db:seed` — verify data exists via `turso db shell <database> "SELECT COUNT(*) FROM users;"`
- Create a temporary test route that queries `db.select().from(users)` and renders results
notes:
- We are using Turso (libsql/SQLite) as the database backend with Drizzle ORM.
- For local development, you can use a local SQLite file (`libsql://./dev.db`) or create a Turso database.
- Turso provides edge-distributed SQLite with fast read replicas — no connection pooling needed.
- Migration files should be committed to git so all environments run the same migrations.
- The seed script should be idempotent. Use `INSERT OR IGNORE` or check existence before inserting.
- SQLite doesn't support `DROP COLUMN` or `ALTER TABLE` extensively — plan schema changes carefully or use Turso's rewrap for migrations.