After Phases 0-2, the codebase is on drizzle-orm@1.0.0-beta.15-859cf75 with:
db.query→ v2 relational query API (new syntax)db._query→ v1 compat shim (old syntax, currently used by all 54 files)
Phase 3 migrates each file from db._query (v1) → db.query (v2), batch by batch.
PR: https://github.com/FlatFilers/obvious/pull/6499
// BEFORE (v1): operator-based
where: eq(schema.threads.id, threadId)
where: and(eq(schema.threads.id, threadId), isNull(schema.threads.deletedAt))
where: or(eq(t.status, 'active'), eq(t.status, 'pending'))
where: inArray(t.id, ids)
where: gt(t.creditsRemaining, '0')
where: isNotNull(t.col)
where: not(eq(t.artifactId, input.artifactId))
where: sql`${schema.webhookSubscriptions.events} @> ${JSON.stringify([eventType])}::jsonb`
// BEFORE (v1): callback-based (same operators, just destructured)
where: (t, { eq, and, isNull }) => and(eq(t.id, id), isNull(t.deletedAt))
// AFTER (v2): object notation
where: { id: threadId }
where: { id: threadId, deletedAt: { isNull: true } }
where: { OR: [{ status: 'active' }, { status: 'pending' }] }
where: { id: { in: ids } }
where: { creditsRemaining: { gt: '0' } }
where: { col: { isNotNull: true } }
where: { artifactId: { ne: input.artifactId } }
where: { RAW: (t) => sql`${t.events} @> ${JSON.stringify([eventType])}::jsonb` }// BEFORE: and(eq(t.workspaceId, wsId), gt(t.credits, '0'), or(isNull(t.expiresAt), gt(t.expiresAt, now)))
// AFTER:
where: {
workspaceId: wsId,
creditsRemaining: { gt: '0' },
OR: [
{ expiresAt: { isNull: true } },
{ expiresAt: { gt: new Date() } }
]
}// BEFORE
orderBy: desc(t.createdAt)
orderBy: [asc(t.name), desc(t.createdAt)]
// AFTER
orderBy: { createdAt: 'desc' }
orderBy: { name: 'asc', createdAt: 'desc' }// Same in both v1 and v2
with: { creator: true }
with: { creator: { columns: { id: true, name: true, email: true } } }
with: { template: true, creator: { columns: { id: true, name: true } } }// Same in both v1 and v2
columns: { id: true, name: true, email: true }Each commit migrates a domain group from db._query → db.query.
| # | Commit Message | Files | ~Queries |
|---|---|---|---|
| 1 | refactor: migrate webhook + slack queries to RQB v2 |
6 files | ~20 |
| 2 | refactor: migrate template queries to RQB v2 |
3 files (template.service.ts is heaviest) | ~40 |
| 3 | refactor: migrate credit + analytics queries to RQB v2 |
3 files | ~22 |
| 4 | refactor: migrate thread + message queries to RQB v2 |
3 files | ~6 |
| 5 | refactor: migrate project + permissions queries to RQB v2 |
6 files | ~35 |
| 6 | refactor: migrate route queries to RQB v2 |
5 files | ~5 |
| 7 | refactor: migrate inngest queries to RQB v2 |
7 files | ~10 |
| 8 | refactor: migrate agent + e2e + auth queries to RQB v2 |
8 files | ~25 |
| 9 | test: update test mocks for RQB v2 |
test files | varies |
apps/api/src/services/webhook-subscription.service.tsapps/api/src/services/webhooks/webhook-authenticator.tsapps/api/src/routes/webhooks/unified.route.tsapps/api/src/routes/slack/status.route.tsapps/api/src/routes/slack/connect.route.tsapps/api/src/routes/slack/handlers/app-mention.handler.tsapps/api/src/routes/slack/handlers/member-joined-channel.handler.tsapps/api/src/routes/slack/handlers/message.handler.tsapps/api/src/routes/slack/link-user.route.tsapps/api/src/routes/slack/link-workspace.route.tsapps/api/src/routes/slack/oauth-callback.route.ts
apps/api/src/services/template.service.ts(heaviest — ~20 queries)apps/api/src/services/template-publish.service.tsapps/api/src/services/template-preview.service.ts
apps/api/src/services/credit-balance.service.tsapps/api/src/services/credits-usage.service.tsapps/api/src/services/analytics.service.ts
apps/api/src/services/thread.service.tsapps/api/src/services/message.service.tsapps/api/src/services/thread-search/indexing.service.ts
apps/api/src/services/project.service.tsapps/api/src/services/permissions.service.tsapps/api/src/services/pinned-artifacts.service.tsapps/api/src/services/published-artifact.service.tsapps/api/src/services/god-mode.service.tsapps/api/src/services/feature-flag.service.ts
apps/api/src/routes/tasks.tsapps/api/src/routes/liveblocks.tsapps/api/src/routes/public-api.route.tsapps/api/src/routes/hydrate.spec.md(if queries in spec)
apps/api/src/inngest/webhook-triggered-task-executor.tsapps/api/src/inngest/slack-agent-response.tsapps/api/src/inngest/objective-rebirth-checker.tsapps/api/src/inngest/obvious-agent-execution.tsapps/api/src/inngest/pdf-indexing.tsapps/api/src/inngest/file-extraction.tsapps/api/src/inngest/artifact-indexing.ts
apps/api/src/agents/obvious-v2/tools/slack-operations.tool.tsapps/api/src/agents/obvious-v2/tools/comments.tool.tsapps/api/src/e2e-harness/multi-agent-orchestration.tsapps/api/src/e2e-harness/sandbox-timeout-scenarios.tsapps/api/src/e2e-harness/shared.tsapps/api/src/e2e-harness/shelldren-scenarios.tsapps/api/src/auth/better-auth.tsapps/api/src/utils/workspace-utils.ts
apps/api/src/services/__tests__/credit-balance.service.test.tsapps/api/src/routes/__tests__/threads-access-control.e2e-test.tsapps/api/src/routes/__tests__/projects.e2e-test.tsapps/api/src/routes/__tests__/thread-folders-access-control.e2e-test.tsapps/api/src/routes/__tests__/checkpoints.e2e-test.tsapps/api/src/routes/__tests__/custom-modes-access-control.e2e-test.tsapps/api/src/services/workbook-export.service.tsapps/api/src/services/e2b-shell.service.tsapps/api/src/services/task.service.tsapps/api/src/services/objective-tracking.service.ts
After each commit:
bun obvious typecheck --changed
bun obvious test --changedAfter all batches complete:
bun run test # full suite
bun obvious typecheck # full typecheck
npx drizzle-kit generate # verify migration gen still works
bun obvious up # verify API starts and queries work- Delete 23 old
relations()blocks fromschema.ts - Remove
import { relations } from 'drizzle-orm/_relations'fromschema.ts - Clean up unused operator imports from migrated files (only where they're truly unused — many files still need
eq,and, etc. fordb.select/update/delete)
1.0.0-beta.2does NOT exist on npm. Thebetadist-tag points to1.0.0-beta.15-859cf75. Always checknpm view drizzle-orm dist-tags --jsonfirst.- Pin the exact version (
1.0.0-beta.15-859cf75), not a range — this is a beta.
drizzle()constructor now requires a config object:drizzle({ client, schema })notdrizzle(client, { schema }). This affected 5 files (db/index.ts, migrate.ts, reset.ts, verify-migrations.ts, e2e-setup.ts).relationsfunction moved from'drizzle-orm'to'drizzle-orm/_relations'for v1 compat. The new v2 API (defineRelations) is still in'drizzle-orm'.$inferSelect/$inferInsertno longer directly indexable on genericPgTableWithColumns<any>. Must useInferSelectModel<T>/InferInsertModel<T>from'drizzle-orm'with a conditional type:T extends Table ? InferSelectModel<T> : never.
PgTableWithColumns<any>alone no longer satisfiesTable<TableConfig<Columns>>in drizzle v1.- Fix: change
T extends PgTableWithColumns<any>→T extends Table & PgTableWithColumns<any>. - Some drizzle utility functions (
getTableColumns,createInsertSchema) need explicitas Tablecasts when called with the genericT.
All are pre-existing patterns exposed by drizzle v1's stricter type inference:
- Implicit
any(22 errors): Column/table iteration APIs (getTableColumns()) now return stricter types. nullvsstring(9 errors): drizzle now correctly types nullable columns asstring | nullinstead ofstring.{} → string(6 errors):inngest step.run()return types interact with drizzle's stricter generics.unknowntypes (8 errors): Similar step.run() interactions producingunknowninstead of expected types.- v2
wheretype mismatch (4 errors):published-artifact.service.tsandtask.service.tspass rawSQLtowhereondb._query— fixed naturally during Phase 3 migration.
drizzle-kit upfailed with "No snapshot was found" despite 162 snapshot files indrizzle/meta/. Journal version is already "1". Likely already at latest format.
- Syntax:
defineRelations(schema, (r) => ({ tableName: { rel: r.one.target({ from: r.source.col, to: r.target.col }) } })) fields→from,references→to(single values for single-column relations)many()with no config for simple reverse relations- Pass both
schemaANDrelationstodrizzle()for the compat bridge:drizzle({ client, schema, relations })
- Do NOT remove old
relations()from schema.ts until all files are migrated —db._querydepends on them. - Many files still need
eq,and,sqlfordb.select/update/delete— only remove imports from files that truly no longer use them. - The 4 type errors for raw SQL in where clauses will fix themselves when migrated to v2 object syntax.
@kubiks/otel-drizzle@^2.1.0peer dep isdrizzle-orm: >=0.28.0(no upper bound) — works fine with v1 beta.