InsForge 共享 Schemas 开发指南:用 Zod 契约统一跨包 API 数据层
【免费下载链接】InsForgeThe all-in-one, open-source backend platform for agentic coding. InsForge gives your coding agent database, auth, storage, compute, hosting, and AI gateway to ship full-stack apps end-to-end.项目地址: https://gitcode.com/GitHub_Trending/in/InsForge
InsForge 是一个面向 Agent 编程场景的开源后端平台(BaaS),其仓库采用 monorepo 结构,包含backend(Express 服务端)、packages/dashboard(管理面板)、packages/ui(UI 组件库)等多个包。当同一个请求/响应/领域数据结构被多个包同时使用时,如何保证前后端契约一致、类型可信、演进可控,就成了工程质量的核心问题。本文基于仓库内维护者指南 .agents/skills/insforge-dev/shared-schemas/SKILL.md,系统讲解@insforge/shared-schemas包的设计定位、组织规范、变更流程与验证手段,并结合仓库真实源码剖析其实现细节。读完本文,你将掌握如何在 InsForge 中新增、修改和同步一个跨包共享契约,并能理解其背后的"以共享包为唯一真源(source of truth)"的工程原则。
一、Shared Schemas 包的定位与适用场景
packages/shared-schemas/是 InsForge 仓库中所有跨包数据契约的唯一来源(source of truth)。它对外发布为 npm 包@insforge/shared-schemas(当前版本1.2.0,见 packages/shared-schemas/package.json),由仓库的 npm workspaces(backend、frontend、packages/*,见 package.json)统一管理。
什么数据应该放进 Shared Schemas
根据 SKILL.md 的 Scope 定义,凡是满足以下任一条件的数据形状,都应定义在本包中:
- 请求(request)载荷:如
POST /api/auth/users的创建用户请求体; - 响应(response)载荷:如登录成功返回的
{ user, accessToken, ... }; - 跨包共享的领域形状(domain shape):如数据库表结构、备份配置、支付订阅等业务实体。
判断标准很简单:如果一个请求、响应或领域形状在 InsForge 的多个表面(backend 路由、dashboard 页面、MCP 工具、SDK)之间共享,就在这里定义它,而不要在包局部文件里复制同一份契约。SKILL.md 特别强调,该包不只服务于仓库内的 backend 和 dashboard 包,还被仓库之外的 InsForge 工具链(如 MCP 与 SDK 代码)消费,因此任何导出的名称和 schema 形状都属于公开契约面(public contract surface),而非内部重构目标。
包的基础设施
包内唯一运行时依赖是zod(^3.23.8),构建产物输出到dist/,并通过exports字段暴露import与types入口(packages/shared-schemas/package.json)。TypeScript 编译配置采用严格模式(strict: true)、ESM 模块、declaration与declarationMap同时开启(packages/shared-schemas/tsconfig.json),保证消费方既能在运行时获得 Zod 校验器,也能在编译期获得精确的类型推导。
二、按领域组织的文件结构
SKILL.md 要求 schema 按领域(domain)组织,并遵循现有的*.schema.ts与*-api.schema.ts双文件拆分模式。从 packages/shared-schemas/src/ 目录可以看到这套命名约定在仓库中的实际落地:
| 领域 | 领域模型文件 | API 契约文件 |
|---|---|---|
| 数据库 | database.schema.ts | database-api.schema.ts |
| 认证 | auth.schema.ts | auth-api.schema.ts |
| AI 网关 | ai.schema.ts | ai-api.schema.ts |
| 日志 | logs.schema.ts | logs-api.schema.ts |
| 函数 | functions.schema.ts | functions-api.schema.ts |
| 存储 | storage.schema.ts | storage-api.schema.ts |
| 密钥 | secrets.schema.ts | secrets-api.schema.ts |
| 实时消息 | realtime.schema.ts | realtime-api.schema.ts |
| 部署 | deployments.schema.ts | deployments-api.schema.ts |
| 定时任务 | schedules.schema.ts | schedules-api.schema.ts |
| 支付 | payments.schema.ts | payments-api.schema.ts |
| 计算服务 | compute-services.schema.ts | compute-services-api.schema.ts |
| 分析 | posthog.schema.ts/posthog-config.schema.ts | posthog-api.schema.ts |
这一拆分的用意在于:*.schema.ts描述领域实体本身(如用户、表、备份记录),与传输层无关;*-api.schema.ts描述HTTP 层契约(请求体、响应体、查询参数、错误响应),依赖前者进行组合。例如auth-api.schema.ts中的createUserRequestSchema就是通过组合auth.schema.ts导出的emailSchema、passwordSchema、nameSchema构建的(packages/shared-schemas/src/auth-api.schema.ts)。
index.ts 是公开 API 的"橱窗"
所有对外导出的符号都统一经过 packages/shared-schemas/src/index.ts 汇总。当前它 re-export 了 29 个 schema 模块,覆盖数据库、认证、存储、AI、日志、函数、实时、部署、支付、计算服务、分析、Web 抓取、错误码、仪表盘事件等几乎全部产品领域。SKILL.md 明确要求:保持index.ts与预期的公开 API 对齐——新增 schema 文件后必须在 index.ts 中导出,否则外部消费者无法引用。
三、核心工作规则:如何正确修改契约
3.1 先想清楚"这属于共享层吗"
修改任何请求/响应/领域形状前,先判断它是否跨包共享。若是,则必须定义在packages/shared-schemas/中;若否(仅在单个包内部使用),才允许留在包局部文件。严禁在 backend 或 dashboard 的局部文件中重复定义同一份契约,否则会出现"两处定义、一处修改、另一处悄然过期"的经典契约漂移问题。
3.2 用 schema 变更触发同步
SKILL.md 将 schema 变更视为一次"同步触发器",修改契约后必须逐项检查下游:
- backend 校验与响应使用:更新 backend/src/api/routes/ 下对应路由对请求体的校验和对响应体的序列化。实际仓库中,
@insforge/shared-schemas已被 40+ 个后端路由文件与中间件引用,覆盖 auth、database、payments、storage、realtime、schedules、secrets 等领域(可从backend/src/api/routes/auth/index.routes.ts等文件的 import 语句确认)。 - dashboard 的服务、hooks 与 UI 假设:更新 packages/dashboard/src/ 下的 API service、数据获取 hooks 及组件中对字段形状的假设。dashboard 的 AI、Auth、Analytics、Datagrid 等特性模块均直接 import 共享包中的类型与校验器。
- 跨包 import 站点排查:在
packages/*、frontend/、backend/全仓库范围内搜索该导出符号的所有引用点,确认没有遗漏。 - 评估下游影响:当变更涉及导出名称、schema 语义或载荷形状时,要明确指出对 MCP、SDK 或仓库外 InsForge 工具链的潜在破坏性影响。
3.3 破坏性变更要保守
SKILL.md 对破坏性变更(breaking change)的态度是"保守":如果确有必要,必须在交接说明(handoff)中显式标注。这一约束与包的公开契约定位直接相关——因为外部消费者(MCP、SDK)无法被本仓库的编译检查覆盖,静默破坏他们的解析逻辑是代价极高的错误。
3.4 永不使用any
SKILL.md 有一条硬性规定:共享契约中禁止使用 TypeScriptany类型。理由很直接:跨包边界上,类型必须是显式且可信的(explicit and trustworthy)。一旦某个字段被标为any,所有下游的类型检查在该字段上都会失明,契约的"可信任性"随之崩塌。仓库中的实际实现也严格遵循此规则,所有 schema 都基于 Zod 原始类型、枚举或组合对象构建,并大量使用z.infer<typeof ...>导出对应 TypeScript 类型。
四、从源码看契约设计的实际落地
4.1 数据库领域:约束的精细建模
packages/shared-schemas/src/database.schema.ts 是理解该包建模哲学的绝佳样本:
- 列类型枚举:
ColumnType枚举覆盖string、date、datetime、integer、float、boolean、uuid、json八种常见列类型,同时columnSchema的type字段用z.union([columnTypeSchema, z.string()])允许自定义类型字符串,兼顾封闭枚举与开放扩展; - 外键的复合键建模:
foreignKeySchema将外键建模为表级约束——一个约束实体包含一个有序的(sourceColumn -> referenceColumn)映射数组referenceColumns,最小长度 1,从而天然支持复合外键;注释明确说明"复合键是包含多对映射的单一实体,绝不跨列重复"; - ON UPDATE 动作完整性:
onUpdateActionSchema与onDeleteActionSchema都接受CASCADE、SET NULL、SET DEFAULT、RESTRICT、NO ACTION五种动作,注释解释了原因——Postgres 的 ON UPDATE 与 ON DELETE 支持相同的参照动作,introspection 可能返回SET NULL/SET DEFAULT,因此 schema 必须接受它们,否则从数据库反读元数据时会校验失败; - 约束即文档:每个 schema 上都有校验信息丰富的错误消息,如列名
max(64)、表名非空、外键至少一个映射、迁移版本号必须匹配/^\d{1,64}$/(支持0001或20260418091500这种时间戳式版本)等,这些消息会直接透传给 API 调用方,构成用户体验的一部分。
4.2 认证领域:请求与响应契约的完备覆盖
packages/shared-schemas/src/auth-api.schema.ts 展示了 API 契约层的典型写法:
- Discriminated Union 处理多方法登录:
createSessionRequestSchema使用z.preprocess将缺失/为空的method字段归一化为'password'(兼容旧客户端),再用z.discriminatedUnion('method', [...])在password与otp两种登录方式间做类型判别。preprocess 注释点明了设计意图:"缺席/空/undefined 的 method 视为传统密码流程;存在但非法的 method 仍会在判别联合中失败"(auth-api.schema.ts); - OTP 六位数字校验:
sixDigitCodeSchema(label)工厂函数生成/^\d{6}$/的正则校验,并为每个使用场景定制错误文案(OTP 验证码、重置密码码等); - PKCE 严格校验:OAuth 初始化与换码请求遵循 RFC 7636,
code_challenge/code_verifier均限制长度 43~128 且必须匹配 base64url 字符集/^[A-Za-z0-9._~-]+$/,并特意用 snake_case 字段名对齐 OAuth 2.0 规范(auth-api.schema.ts); - SMTP 条件校验:
upsertSmtpConfigRequestSchema用superRefine实现条件校验——当enabled: false时允许保存空连接字段(用户主动停用,字段无意义);启用时则强制 host、username、senderName 非空、senderEmail 合法,端口限定为 25/465/587/2525 四选一; - 最小暴露原则:
getPublicAuthConfigResponseSchema从 admin 响应中.omit({ allowedRedirectUrls, smtpConfig }),因为该路由无需认证,任何敏感字段(如内网 SMTP 主机名)都会泄露基础设施信息。注释中的约定值得注意:"新管理员专属字段默认落在authConfigSchema并自动进入 admin 响应;要公开某个字段,必须主动从.omit()中移除它——忘记思考时的默认值是安全的"(auth-api.schema.ts)。
这套"默认隐藏、主动暴露"的安全设计,正是共享契约层能够同时服务内部管理与外部客户端的关键。
4.3 类型推导与消费方式
每个 schema 文件末尾都会用z.infer导出强类型,如TableSchema、ColumnSchema、CreateUserRequest、CreateSessionResponse等。消费方(backend 路由、dashboard service)既可以 import 运行时校验器做请求体验证,也可以 import 类型做响应序列化约束,一套定义、两处受益。dashboard 侧的services/*.service.ts、hooks/use*.ts与组件层大量采用这种模式,例如 AI 特性模块的模型网关配置、Auth 页面的 SMTP 与 OAuth 配置表单。
五、变更后的验证清单
SKILL.md 给出了提交前必须通过的验证命令:
# 1. 构建共享 schemas 包,确认新契约可被编译为声明与产物 cd packages/shared-schemas && npm run build # 2. backend 全量类型检查,确认所有路由消费方无类型错误 cd backend && npx tsc --noEmit # 3. dashboard 类型检查,确认 UI 层消费方无类型错误 cd packages/dashboard && npm run typecheck此外,若某个行为逻辑发生了变化,需要运行对应包的针对性测试(如 backend/tests/unit/ 下的单元测试)。SKILL.md 特别提醒:如果外部消费者(如 MCP、SDK)无法在本仓库内完成验证,必须如实说明,而不是暗示它们已被覆盖——这是对"事实准确"原则的工程化延伸,避免发布未经验证的契约。
在 monorepo 根目录下,也可以直接使用npm run build/npm run typecheck(通过 Turbo 编排所有 workspace,见 package.json)进行全仓统一校验。
六、实操场景:新增一个共享契约的完整流程
综合 SKILL.md 的工作规则与仓库现有模式,新增共享契约的推荐流程如下:
- 判定归属:确认新数据形状跨包共享。例如要给 dashboard 新增一个"工作区设置"实体,backend 路由与 dashboard 页面都需要它,则应放入共享层;
- 建文件:在
packages/shared-schemas/src/下新建workspace.schema.ts(领域实体)与workspace-api.schema.ts(请求/响应契约),复用auth.schema.ts、database.schema.ts等既有模块导出的基础 schema 组合,保持类型显式、不加any; - 导出:在
packages/shared-schemas/src/index.ts中追加export * from './workspace.schema.js';与export * from './workspace-api.schema.js';(注意 ESM 下使用.js后缀); - 后端接入:在 backend/src/api/routes/ 对应路由中 import 请求 schema 做
safeParse校验,import 响应 schema 约束输出形状; - 前端接入:在 packages/dashboard/src/ 的 service 与 hooks 中 import 同一份 schema,让请求构造与响应解析共用一套定义;
- 全局排查:在
packages/*、frontend/、backend/下搜索该导出名,确认所有引用点已更新;评估对仓库外 MCP/SDK 的影响并显式说明; - 验证:按上文清单依次执行
npm run build(shared-schemas)、npx tsc --noEmit(backend)、npm run typecheck(dashboard),并运行受影响模块的测试。
七、总结
@insforge/shared-schemas是 InsForge monorepo 中连接 backend、dashboard 与外部工具链(MCP/SDK)的"契约中枢"。它通过 Zod 将每个跨包数据形状固化为"运行时校验器 + 编译期类型"的双重约束,用领域化的双文件结构(*.schema.ts/*-api.schema.ts)保持组织清晰,用"index.ts 即公开 API 橱窗"保证导出可控,用"默认隐藏敏感字段"的安全约定守护未认证端点,并用"禁止any+ 保守破坏性变更 + 强制全仓同步"三条铁律维持跨包边界的可信度。无论你是维护 backend 路由、dashboard 页面,还是计划扩展 InsForge 的 MCP/SDK 工具链,遵循 SKILL.md 中"以共享包为唯一真源"的原则,都能让每一次契约变更可追溯、可验证、可安全演进。
【免费下载链接】InsForgeThe all-in-one, open-source backend platform for agentic coding. InsForge gives your coding agent database, auth, storage, compute, hosting, and AI gateway to ship full-stack apps end-to-end.项目地址: https://gitcode.com/GitHub_Trending/in/InsForge
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考