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

5.2 KiB

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:
    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:
    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.