Files
GearBox/.planning/phases/14-postgresql-migration/14-CONTEXT.md

5.3 KiB

Phase 14: PostgreSQL Migration - Context

Gathered: 2026-04-04 Status: Ready for planning

## Phase Boundary

Replace SQLite with PostgreSQL as the sole database. Make all database operations async. Establish PGlite-based test infrastructure. Provide a one-time data migration script and Docker Compose for local Postgres development.

## Implementation Decisions

Migration Strategy

  • D-01: Clean rewrite of src/db/schema.ts using drizzle-orm/pg-core (pgTable, serial, text, numeric, timestamp, etc.) — not a conversion of the SQLite schema
  • D-02: Start fresh Postgres migration history in a new directory (e.g., drizzle-pg/) — keep existing drizzle/ SQLite migrations archived for reference
  • D-03: src/db/index.ts switches from bun:sqlite + drizzle-orm/bun-sqlite to drizzle-orm/node-postgres (or drizzle-orm/postgres-js) with async connection

Data Migration Script

  • D-04: Standalone TypeScript script (e.g., scripts/migrate-sqlite-to-postgres.ts) that reads from SQLite file and writes to Postgres — not a Drizzle migration
  • D-05: Script handles type conversions: integer timestamps → proper Postgres timestamp columns, real weight → numeric or double precision, text → text
  • D-06: Script preserves all IDs and foreign key relationships — no ID remapping

Test Infrastructure

  • D-07: createTestDb() returns an async PGlite-backed Drizzle instance — same API shape as current, but async
  • D-08: Per-test fresh PGlite instance with migrations applied (matches current in-memory SQLite pattern, avoids test pollution)
  • D-09: All service and route tests updated from sync to async database operations

Docker Compose

  • D-10: Separate docker-compose.dev.yml for development with Postgres service — keep existing docker-compose.yml for production (updated to include Postgres)
  • D-11: PostgreSQL 16 (latest stable)
  • D-12: Environment variable DATABASE_URL for Postgres connection string (replaces DATABASE_PATH for SQLite)

Claude's Discretion

  • Drizzle Postgres driver choice (node-postgres vs postgres-js) — pick based on Bun compatibility and async performance
  • PGlite configuration details (version, extensions)
  • Column type mapping specifics beyond the ones called out (e.g., whether to use serial vs integer().primaryKey())
  • Migration script error handling and progress reporting
  • Whether to use drizzle-orm/pglite driver or generic pg driver for tests

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Database Schema & Config

  • src/db/schema.ts — Current SQLite schema (source of truth for tables/columns to migrate)
  • src/db/index.ts — Current database initialization (bun:sqlite + drizzle)
  • drizzle.config.ts — Current Drizzle Kit config (sqlite dialect)
  • drizzle/ — Existing SQLite migration files (10 migrations, reference only)

Test Infrastructure

  • tests/helpers/db.ts — Current test database helper (in-memory SQLite, migration application, seed)

Services (all need sync → async)

  • src/server/services/*.ts — 9 service files that use synchronous Drizzle operations
  • src/server/routes/*.ts — 9 route files that call services

Tests (all need updating)

  • tests/services/*.test.ts — 9 service test files
  • tests/routes/*.test.ts — 8 route test files
  • tests/mcp/tools.test.ts — MCP tools test

Docker

  • docker-compose.yml — Current production compose (SQLite volumes, no Postgres)

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • Drizzle ORM already in use — schema definition pattern transfers directly to pg-core
  • Service layer architecture with DI (db as first param) — makes swapping the db instance straightforward
  • Zod schemas in src/shared/schemas.ts — validation layer is database-agnostic, no changes needed
  • TanStack Query hooks — frontend is fully decoupled from database, no changes needed

Established Patterns

  • Service DI pattern: All services take db as first parameter — this means swapping SQLite for Postgres only requires changing what db is, not how services use it
  • Sync Drizzle calls: Current code uses .run(), .get(), .all() synchronously — Postgres requires .execute() / await on all queries
  • Test pattern: createTestDb() creates isolated DB, applies migrations, seeds — same pattern works with PGlite
  • Timestamps as integers: { mode: "timestamp" } on integer columns — Postgres can use native timestamp type

Integration Points

  • src/db/index.ts — Single point of database creation (good: only one file to change for connection)
  • src/server/index.ts — Where db is provided to Hono context via middleware
  • tests/helpers/db.ts — Single test DB factory (good: only one file to change for test infra)
  • drizzle.config.ts — Needs dialect change from sqlite to postgresql

</code_context>

## Specific Ideas

No specific requirements — open to standard approaches for SQLite-to-Postgres migration with Drizzle ORM.

## Deferred Ideas

None — discussion stayed within phase scope


Phase: 14-postgresql-migration Context gathered: 2026-04-04