使用 SDK 资源(类型与配置)
twenty-sdk 提供你在应用中使用的类型化构件和辅助函数。 以下是你最常接触的关键部分。辅助函数
该 SDK 提供辅助函数用于定义你的应用实体。 如 实体检测 中所述,你必须使用export default define<Entity>({...}) 才能让你的实体被检测到:
| 函数 | 目的 |
|---|---|
defineApplication | 配置应用元数据(必需,每个应用一个) |
defineObject | 定义带字段的自定义对象 |
defineLogicFunction | 定义带处理程序的逻辑函数 |
definePreInstallLogicFunction | 定义一个安装前逻辑函数(每个应用一个) |
definePostInstallLogicFunction | 定义一个安装后逻辑函数(每个应用一个) |
defineFrontComponent | 为自定义 UI 定义前端组件 |
defineRole | 配置角色权限和对象访问 |
defineField | 为现有对象扩展额外字段 |
defineView | 为对象定义已保存的视图 |
defineNavigationMenuItem | 定义侧边栏导航链接 |
defineSkill | 定义 AI 智能体技能 |
定义对象
自定义对象同时描述工作空间中记录的架构与行为。 使用defineObject() 以内置校验定义对象:
- 使用
defineObject()以获得内置校验和更好的 IDE 支持。 universalIdentifier必须在各次部署间保持唯一且稳定。- 每个字段都需要
name、type、label以及其自身稳定的universalIdentifier。 fields数组是可选的——你可以定义没有自定义字段的对象。- 你可以使用
yarn twenty entity:add脚手架创建新对象,它会引导你完成命名、字段和关系。
基础字段会自动创建。 当你定义自定义对象时,Twenty 会自动添加标准字段
例如
id、name、createdAt、updatedAt、createdBy、updatedBy 和 deletedAt。
你无需在 fields 数组中定义这些字段——只需添加你的自定义字段。
你可以通过在你的 fields 数组中定义一个同名字段来覆盖默认字段,
但不建议这样做。应用配置(application-config.ts)
每个应用都有一个application-config.ts 文件,用于描述:
- 应用的身份:标识符、显示名称和描述。
- 函数如何运行:它们用于权限的角色。
- (可选)变量:以环境变量形式提供给函数的键值对。
- (可选)安装前函数:在应用安装之前运行的逻辑函数。
- (可选)安装后函数:在应用安装后运行的逻辑函数。
defineApplication() 定义你的应用配置:
universalIdentifier字段是你拥有的确定性 ID;生成一次并在多次同步中保持稳定。applicationVariables会变成函数可用的环境变量(例如,DEFAULT_RECIPIENT_NAME可作为process.env.DEFAULT_RECIPIENT_NAME使用)。defaultRoleUniversalIdentifier必须与角色文件一致(见下文)。- 清单构建期间会自动检测安装前和安装后函数。 参见 安装前函数 和 安装后函数。
角色和权限
应用可以定义角色,以封装对工作空间对象与操作的权限。application-config.ts 中的 defaultRoleUniversalIdentifier 字段指定你的应用逻辑函数所使用的默认角色。
- 作为
TWENTY_API_KEY注入的运行时 API 密钥源自该默认函数角色。 - 类型化客户端将受限于该角色授予的权限。
- 遵循最小权限原则:仅授予函数所需权限来创建一个专用角色,然后引用其通用标识符。
默认函数角色(*.role.ts)
当你脚手架生成新应用时,CLI 也会创建一个默认角色文件。 使用defineRole() 定义带内置校验的角色:
universalIdentifier 会在 application-config.ts 中被引用为 defaultRoleUniversalIdentifier。 换句话说:
- *.role.ts 定义默认函数角色可以执行的操作。
- application-config.ts 指向该角色,使你的函数继承其权限。
- 从脚手架生成的角色开始,然后按照最小权限原则逐步收紧权限。
- 将
objectPermissions和fieldPermissions替换为你的函数所需的对象/字段。 permissionFlags控制对平台级能力的访问。 尽量保持最小化;仅添加所需项。- 在 Hello World 应用中查看可运行示例:
packages/twenty-apps/hello-world/src/roles/function-role.ts。
逻辑函数的配置与入口点
每个函数文件都使用defineLogicFunction() 导出包含处理程序和可选触发器的配置。
- route:在**
/s/端点**下通过 HTTP 路径与方法公开你的函数:
例如path: '/post-card/create',-> 调用<APP_URL>/s/post-card/create
- cron:使用 CRON 表达式按计划运行你的函数。
- databaseEvent:在工作空间对象生命周期事件上运行。 当事件操作为
updated时,可以在updatedFields数组中指定要监听的特定字段。 如果未定义或为空,任何更新都会触发该函数。
例如 person.updated
备注:
triggers数组是可选的。 没有触发器的函数可作为实用函数,被其他函数调用。- 你可以在单个函数中混用多种触发器类型。
安装前函数
安装前函数是在你的应用安装到工作区之前自动运行的逻辑函数。 这对于执行验证任务、先决条件检查,或在主安装开始前准备工作区状态很有用。 当你使用create-twenty-app 脚手架创建一个新应用时,会在 src/logic-functions/pre-install.ts 为你生成一个安装前函数:
- 安装前函数使用
definePreInstallLogicFunction()—— 这是一个省略触发器设置(cronTriggerSettings、databaseEventTriggerSettings、httpRouteTriggerSettings、isTool)的专用变体。 - 处理器会接收一个
InstallLogicFunctionPayload,其包含{ previousVersion: string }—— 即之前安装的应用版本(全新安装则为空字符串)。 - 每个应用仅允许一个安装前函数。 如果检测到多个,清单构建将报错。
- 在构建期间,函数的
universalIdentifier会自动设置为应用清单上的preInstallLogicFunctionUniversalIdentifier—— 你无需在defineApplication()中引用它。 - 默认超时时间设置为 300 秒(5 分钟),以便支持更长的准备任务。
- 安装前函数不需要触发器——它们会在安装前由平台调用,或通过
function:execute --preInstall手动调用。
安装后函数
安装后函数是在你的应用安装到工作区后自动运行的逻辑函数。 这对于一次性设置任务很有用,例如填充默认数据、创建初始记录或配置工作区设置。 当你使用create-twenty-app 脚手架创建一个新应用时,会在 src/logic-functions/post-install.ts 为你生成一个安装后函数:
- 安装后函数使用
definePostInstallLogicFunction()—— 这是一个省略触发器设置(cronTriggerSettings、databaseEventTriggerSettings、httpRouteTriggerSettings、isTool)的专用变体。 - 处理器会接收一个
InstallLogicFunctionPayload,其包含{ previousVersion: string }—— 即之前安装的应用版本(全新安装则为空字符串)。 - 每个应用仅允许一个安装后函数。 如果检测到多个,清单构建将报错。
- 在构建期间,函数的
universalIdentifier会自动设置为应用清单上的postInstallLogicFunctionUniversalIdentifier—— 你无需在defineApplication()中引用它。 - 默认超时时间设置为 300 秒(5 分钟),以便支持更长的设置任务,如数据填充。
- 安装后函数不需要触发器——它们会在安装过程中由平台调用,或通过
function:execute --postInstall手动调用。
路由触发器负载
当路由触发器调用你的逻辑函数时,它会接收一个遵循 AWS HTTP API v2 格式的RoutePayload 对象。 从 twenty-sdk 导入该类型:
RoutePayload 类型具有以下结构:
| 属性 | 类型 | 描述 |
|---|---|---|
headers | Record<string, string | undefined> | HTTP 请求头(仅限 forwardedRequestHeaders 中列出的那些) |
queryStringParameters | Record<string, string | undefined> | 查询字符串参数(多个值以逗号连接) |
pathParameters | Record<string, string | undefined> | 从路由模式中提取的路径参数(例如,/users/:id → { id: '123' }) |
body | object | null | 已解析的请求体(JSON) |
isBase64Encoded | boolean | 请求体是否为 base64 编码 |
requestContext.http.method | string | HTTP 方法(GET、POST、PUT、PATCH、DELETE) |
requestContext.http.path | string | 原始请求路径 |
转发 HTTP 请求头
出于安全原因,默认不会将传入请求的 HTTP 请求头传递给你的逻辑函数。 如需访问特定请求头,请在forwardedRequestHeaders 数组中显式列出:
请求头名称会被规范化为小写。 请使用小写键访问它们(例如,
event.headers['content-type'])。- 脚手架生成:运行
yarn twenty entity:add并选择添加新逻辑函数的选项。 这将生成一个包含处理程序和配置的入门文件。 - 手动:创建一个新的
*.logic-function.ts文件,并使用defineLogicFunction(),遵循相同的模式。
将逻辑函数标记为工具
逻辑函数可以作为供 AI 智能体和工作流使用的工具对外提供。 当函数被标记为工具时,Twenty 的 AI 功能即可发现它,并可在工作流自动化中将其选作一个步骤。 要将逻辑函数标记为工具,请设置isTool: true,并提供 toolInputSchema,使用 JSON Schema 描述预期的输入参数:
isTool(boolean, 默认:false): 当设置为true时,该函数会被注册为工具,并可供 AI 代理和工作流自动化使用。toolInputSchema(object, 可选): 描述函数可接受参数的 JSON Schema 对象。 AI 代理使用此架构来理解该工具期望的输入并验证调用。 如果省略,架构将默认为{ type: 'object', properties: {} }(无参数)。- 设置为
isTool: false(或未设置)的函数不会被暴露为工具。 它们仍可直接执行或被其他函数调用,但不会出现在工具发现中。 - 工具命名: 当作为工具对外暴露时,函数名会被自动规范化为
logic_function_<name>(转换为小写,非字母数字字符替换为下划线)。 例如,enrich-company将变为logic_function_enrich_company。 - 你可以将
isTool与触发器结合使用——一个函数既可以作为工具(由 AI 代理调用),也可以同时由事件(cron、数据库事件、路由)触发。
写一个好的
description。 AI 代理会依赖该函数的 description 字段来决定何时使用该工具。 明确说明该工具的作用以及应在何时调用。前端组件
前端组件使你可以构建在 Twenty 的 UI 中渲染的自定义 React 组件。 使用defineFrontComponent() 以内置校验定义组件:
- 前端组件是在 Twenty 中的隔离上下文中渲染的 React 组件。
component字段引用你的 React 组件。- 组件会在
yarn twenty app:dev期间自动构建并同步。
- 脚手架生成:运行
yarn twenty entity:add并选择添加新前端组件的选项。 - 手动:创建一个新的
.tsx文件,并使用defineFrontComponent(),遵循相同的模式。
技能
技能定义了可复用的指令和能力,AI 智能体可在你的工作区中使用。 使用defineSkill() 定义带内置校验的技能:
name是该技能的唯一标识字符串(推荐使用 kebab-case)。label是在 UI 中显示的人类可读名称。content包含技能指令——这是 AI 智能体使用的文本。icon(可选)设置在 UI 中显示的图标。description(可选)提供有关技能用途的更多上下文。
- 脚手架生成:运行
yarn twenty entity:add并选择添加新技能的选项。 - 手动:创建一个新文件,并使用
defineSkill(),遵循相同的模式。
生成的类型化客户端
两个类型化客户端由yarn twenty app:dev 自动生成(基于你的工作区架构),并存放在 node_modules/twenty-sdk/generated:
CoreApiClient— 查询/graphql端点以获取工作区数据MetadataApiClient— 查询/metadata端点以获取工作区配置并处理文件上传
yarn twenty app:dev 都会自动重新生成这两个客户端。
逻辑函数中的运行时凭据
当你的函数在 Twenty 上运行时,平台会在代码执行前将凭据作为环境变量注入:TWENTY_API_URL:你的应用所针对的 Twenty API 的基础 URL。TWENTY_API_KEY:作用域限定于你的应用默认函数角色的短期密钥。
- 你无需向生成的客户端传递 URL 或 API 密钥。 它会在运行时从 process.env 读取
TWENTY_API_URL和TWENTY_API_KEY。 - API 密钥的权限由
application-config.ts中通过defaultRoleUniversalIdentifier引用的角色决定。 这是你的应用逻辑函数使用的默认角色。 - 应用可以定义角色以遵循最小权限原则。 仅授予函数所需的权限,然后将
defaultRoleUniversalIdentifier指向该角色的通用标识符。
上传文件
生成的MetadataApiClient 包含一个 uploadFile 方法,用于将文件附加到你的工作区对象的文件类型字段。 由于标准 GraphQL 客户端不原生支持多部分文件上传,该客户端提供了一个专用方法,在底层实现了 GraphQL 多部分请求规范。
| 参数 | 类型 | 描述 |
|---|---|---|
fileBuffer | Buffer | 原始文件内容 |
filename | string | 文件名称(用于存储和显示) |
contentType | string | 文件的 MIME 类型(如果省略,默认为 application/octet-stream) |
fieldMetadataUniversalIdentifier | string | 你的对象上文件类型字段的 universalIdentifier |
uploadFile方法可在MetadataApiClient上使用,因为上传 mutation 由/metadata端点解析。- 它使用该字段的
universalIdentifier(而不是其工作区特定的 ID),因此你的上传代码可以在安装了你的应用的任何工作区中使用——这与应用在其他地方引用字段的方式保持一致。 - 返回的
url是一个签名 URL,你可以用它来访问已上传的文件。