-
Notifications
You must be signed in to change notification settings - Fork 2
ADR 003 Database and ORM
Claude product-architect (Opus 4.6) edited this page Feb 22, 2026
·
2 revisions
Accepted
Cornerstone uses SQLite as its database (a hard requirement for simplicity and self-hosted deployment). The decision here is about how to interact with SQLite from TypeScript:
- Type-safe queries that catch schema mismatches at compile time
- Schema migration support for incremental schema evolution
- Lightweight overhead appropriate for a small application
- Good developer experience with autocomplete and type inference
Raw better-sqlite3
- Zero abstraction overhead; direct synchronous SQLite access
- No type safety on queries -- SQL strings with manual type assertions
- No migration tooling; would need a separate migration runner
- Best performance but worst developer experience
- Higher risk of runtime SQL errors
Knex.js (query builder)
- Mature query builder with migration support
- Works well with better-sqlite3 via
knex-better-sqlite3 - Limited TypeScript type inference -- query results need manual typing
- No schema-to-type generation
- Good for complex raw SQL but less ergonomic for standard CRUD
Drizzle ORM
- Type-safe schema definitions that generate TypeScript types
- Lightweight "thin ORM" approach -- stays close to SQL semantics
- First-class SQLite support via
drizzle-orm/better-sqlite3 - Migration tooling via
drizzle-kit(generate, migrate, push) - Schema-driven: define tables in TypeScript, get typed queries automatically
- Supports raw SQL escape hatches when needed
- Active development with frequent releases
Prisma
- Full-featured ORM with code generation
- Heavy runtime (Prisma Engine binary) -- overkill for SQLite
- Schema defined in
.prismafiles (not TypeScript) - Adds significant container size and startup time
- Better suited for larger applications with PostgreSQL/MySQL
Use Drizzle ORM with better-sqlite3 as the underlying SQLite driver.
Drizzle-kit will be used for generating and running migrations. The schema is defined in TypeScript (server/src/db/schema.ts), and migrations are generated into server/src/db/migrations/.
- Full type safety from schema definition through query results
- Schema changes are validated at compile time
-
drizzle-kit generatecreates SQL migration files automatically from schema diffs - Lightweight runtime -- no binary engine or code generation step
- Synchronous SQLite access via better-sqlite3 (no async overhead for DB calls)
- SQL-like API means the team can reason about generated queries easily
- Drizzle is newer than Knex/Prisma; some edge cases may be less documented
-
drizzle-kitCLI occasionally has rough edges with migration ordering - The team needs to learn Drizzle's API (though it maps closely to SQL)
- better-sqlite3 requires native compilation (handled in Docker build)