refactor: 降级 Drizzle ORM 至 0.45.x 稳定版,对齐 Better Auth 兼容性

- drizzle-orm 1.0.0-beta.15 → 0.45.2, drizzle-kit → 0.31.10
- RQBv2 defineRelations() → 旧版 relations() 回调语法
- drizzle-orm/zod → drizzle-zod 独立包
- auth/schema.ts 改由 Better Auth CLI 生成(bun run db:auth)
- db/schema/index.ts 选择性导出表(不导出生成文件中的旧版 relations)
- 删除 db:push script,强制 db:generate → db:migrate 工作流
- 重建迁移基线(删除旧迁移目录,全新生成初始迁移)
This commit is contained in:
2026-03-31 20:18:15 +08:00
parent 5e65c37a26
commit 6bedc1d60d
19 changed files with 879 additions and 1141 deletions
+20 -17
View File
@@ -8,7 +8,7 @@ TanStack Start fullstack web app with ORPC (contract-first RPC) and shadcn/ui.
- **Framework**: TanStack Start (React 19 SSR, file-based routing)
- **Styling**: Tailwind CSS v4 + shadcn/ui (base-nova style, `@base-ui/react`)
- **Database**: PostgreSQL + Drizzle ORM v1 beta (`drizzle-orm/postgres-js`, RQBv2)
- **Database**: PostgreSQL + Drizzle ORM 0.45.x (`drizzle-orm/postgres-js`)
- **State**: TanStack Query v5 (with MutationCache auto-invalidation)
- **RPC**: ORPC (contract-first, type-safe)
- **Auth**: Better Auth (email+password, single-owner, self-hosted)
@@ -99,7 +99,7 @@ src/
### 1. Define Contract (in module: `src/modules/feature/contract.ts`)
```typescript
import { oc } from '@orpc/contract'
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-orm/zod'
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-zod'
import { z } from 'zod'
import * as schema from '@/modules/feature/schema'
import { generatedFieldKeys } from '@/server/db/fields'
@@ -128,8 +128,8 @@ export const myResource = {
.use(dbMiddleware).use(authMiddleware)
.handler(async ({ context }) => {
return await context.db.query.myTable.findMany({
where: { userId: context.user.id },
orderBy: { createdAt: 'desc' },
where: (t, { eq }) => eq(t.userId, context.user.id),
orderBy: (t, { desc }) => desc(t.createdAt),
})
}),
@@ -244,14 +244,16 @@ toast.success('操作成功')
toast.error('操作失败')
```
## Database (Drizzle ORM v1 beta)
## Database (Drizzle ORM 0.45.x)
- **Driver**: `drizzle-orm/postgres-js` (NOT `bun-sql`)
- **Validation**: `drizzle-orm/zod` (built-in, NOT separate `drizzle-zod` package)
- **Relations**: `defineRelations()` in `src/server/db/relations.ts`RQBv2 object syntax
- **Validation**: `drizzle-zod` (separate package, NOT `drizzle-orm/zod`)
- **Relations**: `relations()` from `drizzle-orm` in `src/server/db/relations.ts`callback syntax
- **Table naming**: No `Table` suffix — `user`, `category`, `bookmark`
- **DB instance**: Module-level singleton `export const db = drizzle(...)` (NOT factory pattern)
- **DB instance**: Module-level singleton `export const db = drizzle(client, { schema })` (NOT factory pattern)
- **Shared fields**: Use `...generatedFields` spread for id/createdAt/updatedAt
- **Auth schema**: Generated by Better Auth CLI (`bun run db:auth`), **never hand-edit**
- **Schema re-export**: `db/schema/index.ts` selectively exports tables only (not relations) from auth schema
- **Migration workflow**: Always `db:generate``db:migrate`. **Never** use `db:push`.
- **Path alias exception**: Files in the Drizzle schema chain (`db/schema/index.ts`, module `schema.ts`) MUST use relative imports — `drizzle-kit` does not resolve `@/*` aliases.
@@ -263,11 +265,12 @@ export const myTable = pgTable('my_table', {
userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
})
// Relations — RQBv2 defineRelations
export const relations = defineRelations(schema, (r) => ({
myTable: {
user: r.one.user({ from: r.myTable.userId, to: r.user.id }),
},
// Relations — callback syntax
export const myTableRelations = relations(myTable, ({ one }) => ({
user: one(user, {
fields: [myTable.userId],
references: [user.id],
}),
}))
```
@@ -293,8 +296,8 @@ Kairos is a self-hosted single-user app. There is NO public registration. The fi
- Use `@/*` path aliases (not relative imports)
- Use `render` prop (NOT `asChild`) for base-ui component delegation
- Use `ORPCError` with proper codes
- Use `drizzle-orm/zod` (NOT `drizzle-zod`)
- Use RQBv2 object syntax for `orderBy` and `where`
- Use `drizzle-zod` for schema validation (NOT `drizzle-orm/zod`)
- Use callback syntax for `orderBy` and `where` in relational queries
- Use `move()` from `@dnd-kit/helpers` for DnD reordering
- Use `useState` callback ref for virtualizer scroll elements inside Dialogs
@@ -302,9 +305,9 @@ Kairos is a self-hosted single-user app. There is NO public registration. The fi
- Add new `src/components/ui/*.tsx` without CLI (use `bunx shadcn@latest add` to scaffold, then freely customize)
- Edit `src/routeTree.gen.ts` (auto-generated)
- Use `asChild` prop (base-ui uses `render`, NOT Radix)
- Import from `drizzle-zod` (use `drizzle-orm/zod`)
- Import from `drizzle-orm/zod` (use `drizzle-zod`)
- Use `drizzle-orm/bun-sql` driver
- Pass `schema` to `drizzle()` constructor (only `relations` needed in RQBv2)
- Hand-edit `src/server/auth/schema.ts` (generated by Better Auth CLI, use `bun run db:auth`)
- Add `Table` suffix to Drizzle table exports
- Use `useRef` for scroll elements inside Dialog/conditional rendering
- Use `db:push` — always use `db:generate``db:migrate`