关于契约生成
分类:4-全栈/01-elysia/现代化开发
发布于:
阅读时间:12 分钟
1. 核心策略:以数据库为底薪,按需“裁剪”
你不需要凭空设计返回结构。既然你有了 Drizzle 定义的数据库表,它就是你的数据原形。
-
默认状态:直接返回数据库的整个 Row(使用
)。createSelectSchema -
需要精简时:用
或t.Pick删掉敏感字段(如密码、密钥)。t.Omit -
需要关联时:用
叠加。t.Object
2. 解决“不知道返回什么”:三个典型的契约模板
你可以把常见的返回需求归纳为这三种“模版”,AI 会根据这些模版帮你补全逻辑。
A. 列表页(通用模版)
如果你不知道列表要返回什么,就固定返回“实体列表 + 总数”。
TypeScript
// 以后所有列表接口都复用这个结构 export const CreateListResponse = <T extends TSchema>(itemSchema: T) => t.Object({ data: t.Array(itemSchema), meta: t.Object({ total: t.Number(), page: t.Number(), pageSize: t.Number() }) }); // 使用时: export const UserListResponse = CreateListResponse(UserSelectSchema);
B. 详情页(数据库原形 + 关联)
当你不知道详情页要返回什么,先返回“数据库全字段”,等写到业务发现需要关联数据时,再回来加一个字段。
TypeScript
export const UserDetailResponse = t.Object({ ...UserSelectSchema.properties, // 还不确定关联什么?先留个 Any 或者可选,写业务时再改 roles: t.Optional(t.Array(t.String())), });
C. 操作结果(极简模版)
对于增删改,如果你不知道返回什么,统一返回一个“状态 + ID”。
TypeScript
export const MutationResponse = t.Object({ success: t.Boolean(), id: t.Optional(t.String()), message: t.Optional(t.String()) });
3. 工作流:先“抄”数据库,后“改”契约
不要尝试一步到位,你可以尝试这种**“渐进式契约开发”**:
-
第一步(粗排):后端直接用 Drizzle 的
作为返回契约。SelectSchema -
第二步(开发):前端开始写页面,发现页面上需要展示一个“所属部门名称”,但数据库里只有
。departmentId -
第三步(修正):回到
,在对应的 Response Schema 里加一行packages/contract。deptName: t.String() -
第四步(对齐):此时后端报错(因为没返回这个字段),前端也报错(因为没处理这个字段)。两边一改,契约达成。
4. 自动化:让脚本帮你“写”初稿
如果你觉得定义每个接口太累,我可以帮你写一个**“契约初稿生成器”**。
这个脚本可以扫描你的 Drizzle Table,自动为每个表生成:
-
一个
输入 (Omit ID)Create -
一个
响应 (Array of Table)List -
一个
输入 (Partial)Update
这样你只需要在生成的初稿上删减,而不是从零开始写。
5. 总结:如何克服“定义每个接口”的恐惧?
-
不要怕改:契约不是写死在石头上的,它是可以随业务流转的 TS 代码。
-
重用大于定义:建立自己的
,把分页、基础响应包装好。helper/query-types.t.model.ts -
AI 辅助:当你定义了
,你告诉 AI:“基于这个表,给我写一个 Elysia 的增删改查契约”,它生成的准确率极高。UserTable
4. 命名与分界点的终极总结
为了让 AI 彻底不迷路,我建议你在生成的契约里遵循以下分界命名法:
-
Schema 对象 (用于后端校验):
-
-> 后端
XxxContract.Create校验。body -
-> 后端
XxxContract.ListQuery校验。query
-
-
Static 类型 (用于前端定义):
-
-> 前端表单的
XxxDTO['Create']类型。useState -
-> 前端列表或详情的渲染类型。
XxxDTO['Response']
-