关于 drizzle-sqlite-scaffold
这项模板技能可用于初始化 Drizzle 与 SQLite 项目,或为现有项目添加表和 CRUD 仓库模块,适合希望统一配置、模式与代码约定的开发者。
主要功能
- 01项目初始化模板
- 02按表生成模式与 CRUD 模板
- 03可选 SQLite 驱动配置
- 04可选 drizzle-zod 验证器
- 05迁移操作指引
适用场景
- 01启动 Drizzle SQLite 项目
- 02为现有模式添加数据表
- 03为现有数据表创建 CRUD 模块
- 04统一手写 Drizzle 配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill drizzle-sqlite-scaffold安装后在 Claude Code 中输入/drizzle-sqlite-scaffold确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config.jsongotchas.mdmetadata.jsonassets/templates/client.better-sqlite3.ts.templateclient.bun-sqlite.ts.templateclient.libsql.ts.templatedrizzle.config.local.ts.templatedrizzle.config.turso.ts.templategitignore.templaterepository.ts.templateschema-index.ts.templatetable.ts.templatevalidators.ts.templatereferences/conventions.md详细介绍
Drizzle SQLite 脚手架
用于在全新项目中引导 Drizzle + SQLite,或在现有项目中添加新表/仓库的参数化模板。每个输出都遵循 references/conventions.md 中记录的约定——显式主键、带索引的外键、relations() 声明、$inferSelect/$inferInsert 导出、timestamp_ms 日期、布尔模式的布尔值、WAL + foreign_keys=ON + busy_timeout pragma、带 HMR 保护的单例客户端,以及使用 .returning() + inArray() + .onConflictDoUpdate() 的 CRUD 辅助函数。
适用场景
以下情况可使用这些模板:
- 开始一个将使用 Drizzle 和 SQLite(任意驱动)的新项目
- 为现有 Drizzle 项目添加新表——表文件应符合现有模式
- 为现有表添加 CRUD 仓库模块
- 重构手动搭建的 Drizzle 设置,例如缺少 pragma、没有
relations(),或手写的User类型与架构定义不一致 - 从其他 ORM(Prisma、Kysely)迁移到 Drizzle,并希望从一开始就保持结构一致
设置
必需参数(首次使用时询问,并保存到 config.json)
| 参数 | 必需 | 默认值 | 取值 |
|---|---|---|---|
driver | 是 | — | better-sqlite3 | libsql | bun-sqlite(D1 的生命周期不同——参见下文“Cloudflare D1”) |
db_url_env | 否 | DATABASE_URL | 环境变量名称 |
schema_dir | 否 | ./src/db/schema | 每个表对应的架构文件 |
repository_dir | 否 | ./src/db/repository | 每个表对应的 CRUD 模块 |
validators_dir | 否 | ./src/db/validators | drizzle-zod 架构(当 with_zod=true 时) |
client_path | 否 | ./src/db/client.ts | 单例客户端模块 |
migrations_dir | 否 | ./drizzle | drizzle-kit 输出目录 |
如果 config.json 中已有值,此技能会使用这些值;否则会通过 AskUserQuestion 询问。
每表参数(每次搭建新表时询问)
| 参数 | 必需 | 默认值 | 描述 |
|---|---|---|---|
name | 是 | — | 单数形式的 kebab-case 名称:user、order-item。用于文件名和 TS 标识符(派生出 name_camel、name_pascal)。 |
table_name | 否 | name 的 snake_case 复数形式 | SQL 表名:users、order_items |
pk | 否 | serial-int | serial-int | uuid | cuid2 | text |
timestamps | 否 | true | 添加 createdAt/updatedAt 列 |
soft_delete | 否 | false | 添加可空的 deletedAt + 部分索引 |
relations | 否 | [] | 相关表名列表——用于扩展 relations() 主体 |
with_zod | 否 | true | 生成 drizzle-zod 验证器文件 |
可用模板
项目初始化模板(仅生成一次)
| 模板 | 输出文件 | 使用时机 |
|---|---|---|
drizzle.config.local.ts.template | drizzle.config.ts | driver 为 better-sqlite3、bun-sqlite,或使用 file: URL 的 libsql |
drizzle.config.turso.ts.template | drizzle.config.ts | driver 为连接到 Turso 的 libsql(远程 libsql: URL) |
client.better-sqlite3.ts.template | {{client_path}} | driver 为 better-sqlite3 |
client.libsql.ts.template | {{client_path}} | driver 为 libsql |
client.bun-sqlite.ts.template | {{client_path}} | driver 为 bun-sqlite |
schema-index.ts.template | {{schema_dir}}/index.ts | 始终(初始为空;添加表时追加导出项) |
gitignore.template | .gitignore(追加) | 始终 |
每张表的模板(每张表生成一次)
| 模板 | 输出文件 | 适用时机 |
|---|---|---|
table.ts.template | {{schema_dir}}/{{name}}.ts | 每张表 |
repository.ts.template | {{repository_dir}}/{{name}}.ts | 每张表 |
validators.ts.template | {{validators_dir}}/{{name}}.ts | with_zod=true 时,每张表 |
使用方法
流程 A — 初始化新项目(运行一次)
-
解析项目参数。 读取
config.json。对于任何未填写的必填字段,通过AskUserQuestion询问用户(只有 driver 是严格必填项;其余字段都有合理的默认值)。 -
先安装运行时和工具,确保渲染后的文件能立即通过类型检查:
# Pick the driver-specific runtime package: npm install drizzle-orm @libsql/client # for libsql npm install drizzle-orm better-sqlite3 # for better-sqlite3 npm install drizzle-orm # bun:sqlite is built into Bun # Dev tools (all drivers): npm install -D drizzle-kit npm install -D @types/better-sqlite3 # better-sqlite3 only npm install -D drizzle-zod zod # if with_zod=true -
根据解析出的
driver选择配置和客户端变体:better-sqlite3→drizzle.config.local.ts.template+client.better-sqlite3.ts.template- 使用
file:URL 的libsql→drizzle.config.local.ts.template+client.libsql.ts.template - 使用远程 Turso URL 的
libsql→drizzle.config.turso.ts.template+client.libsql.ts.template bun-sqlite→drizzle.config.local.ts.template+client.bun-sqlite.ts.template
-
渲染并写入项目初始化文件:
drizzle.config.ts{{client_path}}(通常为src/db/client.ts){{schema_dir}}/index.ts(空的汇总文件)- 将
gitignore.template中的代码块追加到项目的.gitignore
-
**对于 libsql:**客户端模板使用顶层
await migrate(...)。确认tsconfig.json中的"module": "ESNext"(或"NodeNext")和"target": "ES2022"+,以支持顶层 await。如果运行时使用 CommonJS,则将顶层 await 替换为一个导出的async function init(),并由应用在启动时调用。 -
将解析出的值保存到
config.json,以便后续添加表时无需再次询问。
流程 B — 添加新表(每张表运行一次)
-
解析每张表的参数。 先询问用户
name,然后为table_name(snake_case 复数)、pk、timestamps、soft_delete、relations、with_zod提供默认值。对于用户希望使用的任何非默认值,使用AskUserQuestion询问。 -
计算派生标识符:
name_camel—name的 camelCase 形式(user、orderItem)name_pascal—name的 PascalCase 形式(User、OrderItem)pk_field— PK 列名(所有 4 种pk模式均为id)pk_ts_type— PK 的 TS 类型(serial-int对应number,uuid/cuid2/text对应string)pk_definition— 实际代码行,例如id: integer().primaryKey({ autoIncrement: true }),(参见下方的 PK 变体表)
-
**渲染表模板:**读取
table.ts.template,替换{{name}}、{{name_camel}}、{{name_pascal}}、{{table_name}}、{{pk_definition}}、{{pk_extra_imports}}等占位符。根据参数展开{{timestamps_block}}和{{soft_delete_block}}(参见下方的“代码块展开”)。写入{{schema_dir}}/{{name}}.ts。 -
**使用相同的参数渲染仓库模板。**写入
{{repository_dir}}/{{name}}.ts。 -
如果
with_zod=true,渲染验证器模板。写入{{validators_dir}}/{{name}}.ts。 -
**追加到 schema 汇总文件:**将
export * from './{{name}}';添加到{{schema_dir}}/index.ts。 -
**生成迁移:**告知用户运行
npx drizzle-kit generate以生成 SQL 文件。提醒用户,如果此脚手架替换了名称不同的现有表,必须明确回答重命名提示。 -
**应用迁移:**针对开发数据库运行
npx drizzle-kit migrate。客户端模板也会在启动时调用migrate(...),但在开发循环中先应用一次,可以在下次进程重启前确认 SQL 能正常运行。
流程 C — 为现有表添加 CRUD 模块(不更改 schema)
与流程 B 的步骤 1-2 相同,但跳过 table.ts.template 渲染,仅生成 repository 模块(以及可选的 validators 模块)。
主键变体
pk 值 | pk_definition | pk_ts_type | 额外导入 |
|---|---|---|---|
serial-int(默认) | id: integer().primaryKey({ autoIncrement: true }), | number | — |
uuid | id: text().primaryKey().$defaultFn(() => crypto.randomUUID()), | string | —(使用 Web Crypto) |
cuid2 | id: text().primaryKey().$defaultFn(() => createId()), | string | import { createId } from '@paralleldrive/cuid2'; |
text | id: text().primaryKey(), | string | —(由调用方提供 ID) |
占位符参考
模板使用的每个 {{placeholder}} 及其派生规则。标记为 简单替换 的项目可直接查找并替换;标记为 块 的项目需要 Agent 按下一节中的规则展开。
| 占位符 | 类型 | 来源 / 派生规则 |
|---|---|---|
{{driver}} | 简单替换 | config.json:driver |
{{db_url_env}} | 简单替换 | config.json:db_url_env |
{{schema_dir}} | 简单替换 | config.json:schema_dir |
{{repository_dir}} | 简单替换 | config.json:repository_dir |
{{validators_dir}} | 简单替换 | config.json:validators_dir |
{{client_path}} | 简单替换 | config.json:client_path |
{{migrations_dir}} | 简单替换 | config.json:migrations_dir |
{{schema_index_import}} | 简单替换 | 派生:client_path → 到 {{schema_dir}}/index.ts 的相对路径(通常为 './schema') |
{{client_import}} | 简单替换 | 派生:从 repository 文件返回 client_path 的相对路径(通常为 '../client') |
{{schema_import}} | 简单替换 | 派生:从 repository 或 validators 文件到对应表文件的相对路径(通常为 '../schema/{{name}}') |
{{name}} | 简单替换 | 每表参数——kebab-case 单数形式(user) |
{{name_camel}} | 简单替换 | 根据 name 派生(user、orderItem) |
{{name_pascal}} | 简单替换 | 根据 name 派生(User、OrderItem) |
{{table_name}} | 简单替换 | 每表参数,默认值为 name 的 snake_case 复数形式(users、order_items) |
{{pk}} | 元数据 | 每表参数——serial-int | uuid | cuid2 | text(决定下面各行的内容) |
{{pk_definition}} | 块 | 实际的 PK 列定义行——参见“PK 变体”表 |
{{pk_field}} | 简单替换 | 此技能提供的四种 PK 变体均为 id |
{{pk_ts_type}} | 简单替换 | serial-int 对应 number,uuid / cuid2 / text 对应 string |
{{pk_extra_imports}} | 块 | serial-int / uuid / text 时为空;cuid2 时为 import { createId } from '@paralleldrive/cuid2'; |
{{relation_imports}} | 块 | 对于 relations[] 中的每张表,生成一行 import { {{relatedCamel}} } from './{{related-kebab}}'; |
{{domain_columns}} | 块 | Agent(或用户)将其替换为实体中实际的非 PK、非时间戳列。如果用户尚未提供这些列,请保留为 TODO 注释。 |
{{timestamps_block}} | 块 | 参见下方展开规则——当 timestamps=true 时生成;当为 false 时删除整行 |
{{soft_delete_block}} | 块 | 参见下方展开规则——当 soft_delete=true 时在列块中生成 |
{{indexes}} | 块 | 每个外键列生成一行 index(...)(以及用户需要的任何 (authorId, publishedAt) 风格的复合索引) |
{{soft_delete_index}} | 块 | deletedAt 上的部分索引(参见下方展开规则);仅当 soft_delete=true 时生成 |
{{relations_body}} | 块 | 每张相关表生成一行 one(...) / many(...);参见下方展开规则 |
{{insert_refinements}} | 块 | INSERT 形状的 drizzle-zod refinement 回调;如果未提供,请保留示例注释 |
{{update_refinements}} | 块 | 与部分 UPDATE 形状相同 |
{{exports}} | 块 | 在 schema-index.ts.template 中,每张表生成一行 export * from './{{name}}';;每次新增表时追加 |
如果模板输出了本表未列出的 {{placeholder}},则说明存在错误——请记录到 gotchas.md 中。
块展开
table.ts.template 使用占位符 块 表示形状可变的部分——Agent 必须根据参数展开,而不能只进行文本替换。
{{timestamps_block}}(当 timestamps=true 时)
createdAt: integer({ mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date()),
updatedAt: integer({ mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date())
.$onUpdateFn(() => new Date()),
当 timestamps=false 时,删除整行(不要保留注释标记)。
{{soft_delete_block}}(当 soft_delete=true 时)
在列块中:
deletedAt: integer({ mode: 'timestamp_ms' }),
在索引块中:
index('{{table_name}}_active_idx').on(table.deletedAt).where(sql`deleted_at IS NULL`),
相应调整 sql 导入。
{{relations_body}}(当 relations 非空时)
对于 relations[] 中的每个关联表,Agent 会根据外键所在的位置判断它是 one 还是 many:如果外键位于当前表中,则为 one;如果位于关联表中,则为 many:
// FK on current table — `one`:
parent: one(parents, { fields: [users.parentId], references: [parents.id] }),
// FK on related table — `many`:
posts: many(posts),
如果用户不容易判断,请默认各提供一个示例,并留下注释。
{{indexes}} 块
对于每个外键列,生成:
index('{{table_name}}_{{column}}_idx').on(table.{{columnCamel}}),
Cloudflare D1 说明
D1 的生命周期有所不同:客户端会在每个请求中根据 env.DB(绑定)创建,而不是作为模块级单例。此技能不提供 D1 客户端模板;请遵循官方 D1 + Drizzle 指南进行接线。table.ts.template、repository.ts.template 和 validators.ts.template 仍可用于 D1,只有客户端模块不同。
参考文件
| 文件 | 说明 |
|---|---|
| references/conventions.md | 强制执行的 11 项约定,包含原因和规则交叉引用 |
| gotchas.md | 随时间发现的边缘情况 |
| metadata.json | 版本和驱动程序参考 |
| config.json | 项目级参数存储 |
相关技能
drizzle-sqlite— 这些模板所编码的库参考规则。约定文档会引用其中的特定规则文件名。需要做出有依据的例外、调试生成的文件,或搭建模板范围之外的内容(自定义迁移、复杂查询、性能工作)时,请阅读此文档。better-auth-scaffold— 在 Drizzle DB 上搭建 Better Auth;此技能提供客户端后,可运行此技能。