-
Notifications
You must be signed in to change notification settings - Fork 15
feat: sqlite drizzle support #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
shadowfax92
commented
Jan 15, 2026
- feat: drizzle support with custom migration
- feat: migrate rater limiter to drizzle and checkin drizzle/ package
- feat: if migrate fails, nuke db and create fresh
Greptile SummaryThis PR migrates the database layer from raw Bun SQLite to Drizzle ORM, adding type-safe database operations and a custom embedded migration system for Bun compile compatibility. Key changes:
Issues found:
The migration is well-designed with proper transaction handling, error recovery, and maintains backward compatibility with existing databases. Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant App as Application
participant Init as initializeDb()
participant Open as openAndMigrate()
participant Mig as runMigrations()
participant Nuke as nukeAndRetry()
participant DB as SQLite Database
participant Drizzle as Drizzle Client
App->>Init: initializeDb(dbPath)
alt First initialization
Init->>Open: openAndMigrate(dbPath)
Open->>DB: new Database(dbPath)
DB-->>Open: sqliteDb
Open->>DB: PRAGMA journal_mode = WAL
Open->>DB: PRAGMA foreign_keys = ON
Open->>Mig: runMigrations(sqliteDb)
Mig->>DB: CREATE TABLE IF NOT EXISTS _migrations
Mig->>DB: SELECT version FROM _migrations
DB-->>Mig: []
loop For each pending migration
Mig->>DB: BEGIN TRANSACTION
Mig->>DB: Execute migration SQL (splitStatements)
Mig->>DB: INSERT INTO _migrations
Mig->>DB: COMMIT
end
Mig-->>Open: Success
Open-->>Init: sqliteDb
Init->>Drizzle: createDrizzleClient(sqliteDb)
Drizzle-->>Init: drizzleDb
Init-->>App: drizzleDb
else Migration fails
Open->>Mig: runMigrations(sqliteDb)
Mig-->>Open: throws error
Open-->>Init: throws error
Init->>Init: catch error, close db
Init->>Nuke: nukeAndRetry(dbPath)
Nuke->>DB: rename db to .bak
Nuke->>DB: delete WAL files
Nuke->>Open: openAndMigrate(dbPath)
Open->>DB: new Database (fresh)
Open->>Mig: runMigrations(sqliteDb)
Mig-->>Open: Success
Open-->>Nuke: sqliteDb
Nuke-->>Init: sqliteDb
Init->>Drizzle: createDrizzleClient(sqliteDb)
Drizzle-->>Init: drizzleDb
Init-->>App: drizzleDb
end
App->>App: Use drizzleDb for queries
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
20 files reviewed, 2 comments
| export const identity = sqliteTable('identity', { | ||
| id: integer('id') | ||
| .primaryKey() | ||
| .$default(() => 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: The .$default(() => 1) is a client-side default in Drizzle ORM, but the generated migration SQL in drizzle/0000_messy_rafael_vega.sql doesn't include the critical CHECK (id = 1) constraint that existed in the old schema. This constraint ensures the identity table only ever has a single row with id = 1, which is essential for the singleton identity pattern.
The drizzle-generated SQL shows:
CREATE TABLE `identity` (
`id` integer PRIMARY KEY NOT NULL,
...
)But the manually written migration in versions.ts correctly includes:
CREATE TABLE IF NOT EXISTS identity (
id INTEGER PRIMARY KEY CHECK (id = 1),
...
)This mismatch means the drizzle/ folder doesn't match versions.ts, which could cause confusion and issues during development or when syncing schemas.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/lib/db/schema/identity.ts
Line: 9:12
Comment:
**logic:** The `.$default(() => 1)` is a client-side default in Drizzle ORM, but the generated migration SQL in `drizzle/0000_messy_rafael_vega.sql` doesn't include the critical `CHECK (id = 1)` constraint that existed in the old schema. This constraint ensures the identity table only ever has a single row with `id = 1`, which is essential for the singleton identity pattern.
The drizzle-generated SQL shows:
```sql
CREATE TABLE `identity` (
`id` integer PRIMARY KEY NOT NULL,
...
)
```
But the manually written migration in `versions.ts` correctly includes:
```sql
CREATE TABLE IF NOT EXISTS identity (
id INTEGER PRIMARY KEY CHECK (id = 1),
...
)
```
This mismatch means the `drizzle/` folder doesn't match `versions.ts`, which could cause confusion and issues during development or when syncing schemas.
How can I resolve this? If you propose a fix, please make it concise.| if (db) { | ||
| db.close() | ||
| db = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: If db fails to open initially (line 57), it will be null here. However, if it partially opened (connection succeeded but migration failed), the db object may exist but be in a bad state. Calling db.close() on a partially-initialized or corrupted connection could throw an error. Wrap the .close() call in a try-catch to prevent the recovery path from failing.
| if (db) { | |
| db.close() | |
| db = null | |
| try { | |
| if (db) { | |
| db.close() | |
| } | |
| } catch { | |
| // Ignore close errors during recovery | |
| } finally { | |
| db = null | |
| } |
Have you tested the recovery path when the database is corrupted mid-open to ensure close() doesn't throw?
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/lib/db/index.ts
Line: 64:66
Comment:
**logic:** If `db` fails to open initially (line 57), it will be `null` here. However, if it partially opened (connection succeeded but migration failed), the `db` object may exist but be in a bad state. Calling `db.close()` on a partially-initialized or corrupted connection could throw an error. Wrap the `.close()` call in a try-catch to prevent the recovery path from failing.
```suggestion
try {
if (db) {
db.close()
}
} catch {
// Ignore close errors during recovery
} finally {
db = null
}
```
Have you tested the recovery path when the database is corrupted mid-open to ensure close() doesn't throw?
How can I resolve this? If you propose a fix, please make it concise.