twenty-sdk package provides defineEntity functions to declare your app’s data model. 你必须使用 export default defineEntity({...}),这样 SDK 才能检测到你的实体。 这些函数会在构建时校验你的配置,并提供 IDE 自动补全和类型安全。
文件组织由你决定。
实体检测基于 AST——无论文件位于何处,SDK 都能找到
export default defineEntity(...) 的调用。 按类型对文件分组(例如 logic-functions/、roles/)只是代码组织的一种约定,并非必需。defineRole
配置角色权限和对象访问
defineRole
配置角色权限和对象访问
角色封装了对你的工作空间对象与操作的权限。
restricted-company-role.ts
defineApplication
配置应用元数据(必需,每个应用一个)
defineApplication
配置应用元数据(必需,每个应用一个)
每个应用必须且只能有一个 备注:
该角色的
defineApplication 调用,用于描述:- 应用的身份:标识符、显示名称和描述。
- 权限:其函数和前端组件所使用的角色。
- (可选)变量:以环境变量形式提供给函数的键值对。
- (可选)安装前/安装后函数:在安装之前或之后运行的逻辑函数。
src/application-config.ts
universalIdentifier字段是你拥有的确定性 ID。 只需生成一次,并在多次同步过程中保持稳定不变。applicationVariables会变成你的函数和前端组件可用的环境变量(例如,DEFAULT_RECIPIENT_NAME可作为process.env.DEFAULT_RECIPIENT_NAME使用)。defaultRoleUniversalIdentifier必须引用使用defineRole()定义的角色(见上文)。- 在构建清单时会自动检测安装前/安装后函数——无需在
defineApplication()中引用它们。
应用市场元数据
如果你计划发布你的应用,这些可选字段将控制你的应用在应用市场中的展示:| 字段 | 描述 |
|---|---|
作者 | 作者或公司名称 |
类别 | 用于应用市场筛选的应用类别 |
logoUrl | 应用徽标的路径(例如 public/logo.png) |
screenshots | 截图路径数组(例如 public/screenshot-1.png) |
aboutDescription | 用于“关于”选项卡的更长的 Markdown 描述。 如果省略,市场将使用该软件包在 npm 上的 README.md。 |
websiteUrl | 你的网站链接 |
termsUrl | 服务条款链接 |
emailSupport | 支持电子邮件地址 |
issueReportUrl | 问题跟踪器链接 |
角色和权限
application-config.ts 中的 defaultRoleUniversalIdentifier 字段指定你的应用的逻辑函数和前端组件所使用的默认角色。 详见上文的 defineRole。- 作为
TWENTY_APP_ACCESS_TOKEN注入的运行时令牌来源于该角色。 - 类型化客户端将受限于该角色授予的权限。
- 遵循最小权限原则:创建一个仅包含你的函数所需权限的专用角色。
默认函数角色
当你使用脚手架创建新应用时,CLI 会创建一个默认角色文件:src/roles/default-role.ts
universalIdentifier 会在 application-config.ts 中被引用为 defaultRoleUniversalIdentifier:- *.role.ts 定义该角色可以执行的操作。
- application-config.ts 指向该角色,使你的函数继承其权限。
- 从脚手架生成的角色开始,然后按照最小权限原则逐步收紧权限。
- 将
objectPermissions和fieldPermissions替换为你的函数所需的对象/字段。 permissionFlags控制对平台级能力的访问。 尽量保持最小化。- 查看一个可运行示例:
hello-world/src/roles/function-role.ts。
defineObject
定义带字段的自定义对象
defineObject
定义带字段的自定义对象
自定义对象同时描述工作空间中记录的架构与行为。 使用 关键点:
defineObject() 以内置校验定义对象:postCard.object.ts
- 使用
defineObject()以获得内置校验和更好的 IDE 支持。 universalIdentifier必须在各次部署间保持唯一且稳定。- 每个字段都需要
name、type、label以及其自身稳定的universalIdentifier。 fields数组是可选的——你可以定义没有自定义字段的对象。- 你可以使用
yarn twenty add脚手架创建新对象,它会引导你完成命名、字段和关系。
基础字段会自动创建。 当你定义自定义对象时,Twenty 会自动添加标准字段
例如
id、name、createdAt、updatedAt、createdBy、updatedBy 和 deletedAt。
你无需在 fields 数组中定义这些字段——只需添加你的自定义字段。
你可以通过在你的 fields 数组中定义一个同名字段来覆盖默认字段,
但不建议这样做。defineField — 标准字段
为现有对象扩展额外字段
defineField — 标准字段
为现有对象扩展额外字段
使用 关键点:
defineField() 向你不拥有的对象添加字段——例如标准的 Twenty 对象(Person、Company 等)。 或来自其他应用的对象。 与在 defineObject() 中的内联字段不同,独立字段需要一个 objectUniversalIdentifier 来指定它们要扩展的对象:src/fields/company-loyalty-tier.field.ts
objectUniversalIdentifier用于标识目标对象。 对于标准对象,请使用从twenty-sdk导出的STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS。- 在
defineObject()中以内联方式定义字段时,你不需要objectUniversalIdentifier——它会从父对象继承。 defineField()是为非通过defineObject()创建的对象添加字段的唯一方式。
defineField — 关联字段
使用双向关系将对象连接在一起
defineField — 关联字段
使用双向关系将对象连接在一起
关系用于将对象彼此连接。 在 Twenty 中,关系始终是双向的——你需要定义两侧,每一侧都引用另一侧。关系有两种类型:
步骤 2:在 PostCardRecipient 上定义 MANY_TO_ONE 侧(“多”侧——持有外键):
| 关系类型 | 描述 | 是否有外键? |
|---|---|---|
MANY_TO_ONE | 该对象的多条记录指向目标对象的一条记录 | 是(joinColumnName) |
ONE_TO_MANY | 该对象的一条记录拥有目标对象的多条记录 | 否(反向侧) |
关系如何工作
每个关系都需要两个相互引用的字段:- MANY_TO_ONE 侧——位于持有外键的对象上
- ONE_TO_MANY 侧——位于拥有集合的对象上
FieldType.RELATION,并通过 relationTargetFieldMetadataUniversalIdentifier 相互交叉引用。示例:Post Card 拥有多个收件人
假设一个PostCard 可以发送到多个 PostCardRecipient 记录。 每个收件人只隶属于一张 Post Card。步骤 1:在 PostCard 上定义 ONE_TO_MANY 侧(“一”侧):src/fields/post-card-recipients-on-post-card.field.ts
src/fields/post-card-on-post-card-recipient.field.ts
**循环导入:**两个关系字段相互引用彼此的
universalIdentifier。 为避免循环导入问题,请在各自文件中将字段 ID 作为具名常量导出,并在另一个文件中导入它们。 构建系统会在编译时解析这些引用。与标准对象建立关系
要与内置的 Twenty 对象(Person、Company 等)建立关系,请使用STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS:src/fields/person-on-self-hosting-user.field.ts
关系字段属性
| 属性 | 必填 | 描述 |
|---|---|---|
类型 | 是 | 必须为 FieldType.RELATION |
relationTargetObjectMetadataUniversalIdentifier | 是 | 目标对象的 universalIdentifier |
relationTargetFieldMetadataUniversalIdentifier | 是 | 目标对象上匹配字段的 universalIdentifier |
universalSettings.relationType | 是 | RelationType.MANY_TO_ONE 或 RelationType.ONE_TO_MANY |
universalSettings.onDelete | 仅适用于 MANY_TO_ONE | 当被引用的记录被删除时的处理方式:CASCADE、SET_NULL、RESTRICT 或 NO_ACTION |
universalSettings.joinColumnName | 仅适用于 MANY_TO_ONE | 外键的数据库列名(例如,postCardId) |
在 defineObject 中内联关系字段
你也可以直接在defineObject() 内定义关系字段。 在这种情况下,省略 objectUniversalIdentifier——它会从父对象继承:Scaffolding entities with yarn twenty add
Instead of creating entity files by hand, you can use the interactive scaffolder:
universalIdentifier and the correct defineEntity() call.
You can also pass the entity type directly to skip the first prompt:
可用的实体类型
| 实体类型 | 命令 | Generated file |
|---|---|---|
| 对象 | yarn twenty add object | src/objects/\<name>.ts |
| 字段 | yarn twenty add field | src/fields/\<name>.ts |
| Logic function | yarn twenty add logicFunction | src/logic-functions/\<name>.ts |
| Front component | yarn twenty add frontComponent | src/front-components/\<name>.tsx |
| 角色 | yarn twenty add role | src/roles/\<name>.ts |
| 技能 | yarn twenty add skill | src/skills/\<name>.ts |
| 代理 | yarn twenty add agent | src/agents/\<name>.ts |
| 视图 | yarn twenty add view | src/views/\<name>.ts |
| 导航菜单项 | yarn twenty add navigationMenuItem | src/navigation-menu-items/\<name>.ts |
| 页面布局 | yarn twenty add pageLayout | src/page-layouts/\<name>.ts |
脚手架生成的内容
每种实体类型都有其自己的模板。 例如,yarn twenty add object 会询问:
- 名称(单数)——例如,
invoice - 名称(复数)——例如,
invoices - 标签(单数)——根据名称自动填充(例如,
Invoice) - 标签(复数)——自动填充(例如,
Invoices) - 创建视图和导航项?——如果你选择是,脚手架还会为新对象生成相应的视图和侧边栏链接。
field 实体类型更为详细:它会询问字段名称、标签、类型(从所有可用字段类型列表中选择,如 TEXT、NUMBER、SELECT、RELATION 等),以及目标对象的 universalIdentifier。
自定义输出路径
使用--path 标志将生成的文件放置在自定义位置: