Create new database tables with Drizzle ORM schemas and Valibot validation. 使用 Drizzle ORM 创建新的数据库表模式和 Valibot 验证。
Use when:
设计思路 / Design Notes:
- 数据库模式是基础设施,需要特别注意类型安全
- 包含 Valibot 验证模式生成,与 API 层集成
- 提供常用字段模式(UUID、时间戳、外键等)
- 强调 snake_case 命名规范
Create Drizzle ORM schemas with type-safe Valibot validation schemas. 创建带有类型安全 Valibot 验证模式的 Drizzle ORM 模式。
File: packages/db/src/schemas/<entity>.ts
// [IN]: drizzle-orm/pg-core, drizzle-valibot, valibot, ./auth / 依赖 Drizzle、验证器及认证模式
// [OUT]: <entity> table, Create<Entity>Schema / 导出 <entity> 表及创建验证模式
// [POS]: Database layer - <Entity> schema / 数据库层 - <实体>模式
// Protocol: When updating me, sync this header + parent folder's .folder.md
// 协议:更新本文件时,同步更新此头注释及所属文件夹的 .folder.md
import { pgTable } from 'drizzle-orm/pg-core';
import { createInsertSchema } from 'drizzle-valibot';
import * as v from 'valibot';
import { user } from './auth';
// ============ Table Definition / 表定义 ============
export const <entity> = pgTable('<entity>', (t) => ({
// Primary key - UUID
id: t.uuid().primaryKey().defaultRandom(),
// Required fields / 必需字段
title: t.varchar({ length: 256 }).notNull(),
content: t.text().notNull(),
// Optional fields / 可选字段
description: t.text(),
// Timestamps / 时间戳
createdAt: t
.timestamp({ mode: 'string', withTimezone: true })
.notNull()
.defaultNow(),
updatedAt: t
.timestamp({ mode: 'string', withTimezone: true })
.notNull()
.defaultNow(),
// Foreign key to user / 用户外键
createdBy: t
.text()
.references(() => user.id)
.notNull(),
}));
// ============ Validation Schemas / 验证模式 ============
// For creating new records (excludes auto-generated fields)
// 用于创建新记录(排除自动生成的字段)
export const Create<Entity>Schema = v.omit(
createInsertSchema(<entity>, {
// Custom validation rules / 自定义验证规则
title: v.pipe(v.string(), v.minLength(3), v.maxLength(256)),
content: v.pipe(v.string(), v.minLength(5), v.maxLength(5000)),
}),
['id', 'createdAt', 'updatedAt', 'createdBy'],
);
// For updating records / 用于更新记录
export const Update<Entity>Schema = v.partial(Create<Entity>Schema);
// TypeScript types / TypeScript 类型
export type <Entity> = typeof <entity>.$inferSelect;
export type New<Entity> = typeof <entity>.$inferInsert;
File: packages/db/src/schema.ts
// Add export / 添加导出
export * from './schemas/<entity>';
File: packages/db/src/schemas/.folder.md
Add the new file to the file list / 将新文件添加到文件列表:
## Files
- `auth.ts`: Local - Authentication tables / 认证表
- `posts.ts`: Local - Post table / 文章表
- `<entity>.ts`: Local - <Entity> table / <实体>表 👈 New
pnpm db:push
id: t.uuid().primaryKey().defaultRandom(),
createdAt: t.timestamp({ mode: 'string', withTimezone: true }).notNull().defaultNow(),
updatedAt: t.timestamp({ mode: 'string', withTimezone: true }).notNull().defaultNow(),
userId: t.text().references(() => user.id).notNull(),
// With cascade delete / 带级联删除
userId: t.text().references(() => user.id, { onDelete: 'cascade' }).notNull(),
status: t.text({ enum: ['draft', 'published', 'archived'] }).notNull().default('draft'),
metadata: t.jsonb().$type<{ key: string; value: unknown }[]>(),
v.pipe(v.string(), v.minLength(1), v.maxLength(100))
v.pipe(v.string(), v.email())
v.pipe(v.string(), v.url())
v.optional(v.string(), 'default value')
packages/db/src/schemas/posts.ts