跳转到主要内容
安装钩子是在安装或升级生命周期期间运行的特殊逻辑函数。 它们与常规的逻辑函数共享相同的处理程序运行时,并接收 InstallPayload,但它们使用自己的定义函数声明——definePostInstallLogicFunction()definePreInstallLogicFunction()——并且存在于普通触发模型(HTTP、cron、数据库事件)之外。 每个应用最多只能定义一个安装前函数最多一个安装后函数。 如果检测到任一类型多于一个,清单构建将报错。
┌─────────────────────────────────────────────────────────────┐
│ install flow                                                │
│                                                             │
│   upload package → [pre-install] → metadata migration →     │
│   generate SDK → [post-install]                             │
│                                                             │
│                  old schema visible    new schema visible   │
└─────────────────────────────────────────────────────────────┘
安装后函数会在你的应用完成安装到某个工作区后自动运行。 服务器会在应用的元数据已同步并已生成 SDK 客户端之后执行它,因此工作区已完全可用,且新架构已就绪。 常见用例包括预置默认数据、创建初始记录、配置工作区设置,或在第三方服务上预配资源。
src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';

const handler = async (payload: InstallPayload): Promise<void> => {
  console.log('Post install logic function executed successfully!', payload.previousVersion);
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Runs after installation to set up the application.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  shouldRunSynchronously: false,
  handler,
});
你也可以随时使用 CLI 手动执行安装后函数:
yarn twenty dev:function:exec --postInstall
关键点:
  • 安装后函数使用 definePostInstallLogicFunction() — 这是一个省略触发器设置(cronTriggerSettingsdatabaseEventTriggerSettingshttpRouteTriggerSettingstoolTriggerSettingsworkflowActionTriggerSettings)的专用变体。
  • 处理程序会接收一个 InstallPayload,其为 { previousVersion?: string; newVersion: string } —— newVersion 是正在安装的版本,而 previousVersion 是先前已安装的版本(在全新安装时为 undefined)。 使用这些值来区分全新安装与升级,并运行特定版本的迁移逻辑。
  • 钩子何时运行:默认情况下,仅在全新安装时运行。 如果还希望在应用从旧版本升级时运行,请传入 shouldRunOnVersionUpgrade: true。 若省略,该标志默认为 false,升级将跳过该钩子。
  • 执行模型 — 默认异步,可选择同步shouldRunSynchronously 标志控制安装后如何执行。
    • shouldRunSynchronously: false (默认) — 该钩子会加入消息队列,设置 retryLimit: 3,并在工作线程中异步运行。 作业一入列,安装响应即返回,因此缓慢或失败的处理程序不会阻塞调用方。 工作线程最多会重试三次。 将其用于长时间运行的作业——预填充大型数据集、调用缓慢的第三方 API、预配外部资源,以及任何可能超出合理 HTTP 响应窗口的任务。
    • shouldRunSynchronously: true — 该钩子会在安装流程中内联执行(与安装前使用相同的执行器)。 安装请求将阻塞直至处理程序完成;若抛出异常,安装调用方将收到 POST_INSTALL_ERROR。 不进行自动重试。 用于需要在响应前完成的快速工作——例如向用户返回验证错误,或进行安装调用返回后客户端将立即依赖的快速设置。 请注意,运行安装后时,元数据迁移已应用完成,因此同步模式下的失败不会回滚架构更改——它只会暴露错误。
  • 确保你的处理程序是幂等的。 在异步模式下,队列最多可重试三次;在任一模式下,当 shouldRunOnVersionUpgrade: true 时,该钩子在升级时可能再次运行。
  • 在处理程序内可使用环境变量 APPLICATION_IDAPP_ACCESS_TOKENAPI_URL(与其他逻辑函数相同),因此你可以使用作用域限定到你应用的应用访问令牌调用 Twenty API。
  • 每个应用仅允许一个安装后函数。 如果检测到多个,清单构建将报错。
  • 构建期间,函数的 universalIdentifiershouldRunOnVersionUpgradeshouldRunSynchronously 会自动附加到应用清单的 postInstallLogicFunction 字段下——你无需在 defineApplication() 中引用它们。
  • 默认超时时间设置为 300 秒(5 分钟),以便支持更长的设置任务,如数据填充。
  • 开发模式下不执行:当应用在本地注册(通过 yarn twenty dev)时,服务器会完全跳过安装流程,并通过 CLI 监视器直接同步文件——因此无论 shouldRunSynchronously 如何,安装后在开发模式下都不会运行。 使用 yarn twenty dev:function:exec --postInstall 在运行中的工作区上手动触发它。
安装前函数会在安装期间自动运行,在应用工作区元数据迁移之前。 它与安装后共享相同的负载结构(InstallPayload),但在安装流程中位置更早,因此可以准备即将到来的迁移所依赖的状态——典型用例如备份数据、验证与新架构的兼容性,或归档即将被重构或删除的记录。
src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';

const handler = async (payload: InstallPayload): Promise<void> => {
  console.log('Pre install logic function executed successfully!', payload.previousVersion);
};

export default definePreInstallLogicFunction({
  universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
  name: 'pre-install',
  description: 'Runs before installation to prepare the application.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: true,
  handler,
});
你也可以随时使用 CLI 手动执行安装前函数:
yarn twenty dev:function:exec --preInstall
关键点:
  • 安装前函数使用 definePreInstallLogicFunction()——与安装后相同的专用配置,只是附加到不同的生命周期阶段。
  • 安装前和安装后处理程序接收相同的 InstallPayload 类型:{ previousVersion?: string; newVersion: string }。 导入一次,可在两个钩子中复用。
  • 钩子何时运行:位于工作区元数据迁移(synchronizeFromManifest)之前。 在执行之前,服务器会运行一次纯增量的“精简同步”,将版本的安装前函数注册到工作区元数据中——不会触及其他任何内容——然后再执行它。 由于此次同步仅为增量操作,当你的处理程序运行时,上一版本的对象、字段和数据仍完好无损:你可以安全地读取并备份迁移前的状态。
  • 执行模型:安装前以同步方式执行,并且会阻塞安装。 如果处理程序抛出异常,安装会在任何架构更改应用之前被中止——工作区将保持在上一版本且处于一致状态。 这是有意为之:安装前是你拒绝高风险升级的最后机会。
  • 与安装后相同,每个应用仅允许一个安装前函数。 在构建期间,它会自动附加到应用清单的 preInstallLogicFunction 下。
  • 开发模式下不执行:与安装后相同——对于本地注册的应用将完全跳过安装流程,因此在 yarn twenty dev 下不会运行安装前。 使用 yarn twenty dev:function:exec --preInstall 手动触发它。
两个钩子都属于同一安装流程,并接收相同的 InstallPayload。 区别在于它们相对于工作区元数据迁移何时运行,这会影响它们可以安全访问的数据范围。安装前始终为同步(会阻塞安装并可中止它)。 安装后默认异步——在工作线程中入列并自动重试——但可通过 shouldRunSynchronously: true 选择同步执行。 关于各模式的使用场景,请参见上方的 definePostInstallLogicFunction 折叠面板。对于需要新架构已存在的任何事项,请使用 post-install 这是最常见的情况:
  • 针对新添加的对象和字段预填充默认数据(创建初始记录、默认视图、演示内容)。
  • 在应用已有凭据的前提下,向第三方服务注册 Webhook。
  • 调用你自己的 API 完成依赖已同步元数据的设置。
  • 用于在每次升级时对状态进行对账的幂等“确保其存在”逻辑——结合 shouldRunOnVersionUpgrade: true 使用。
示例——在安装后预填充一个默认的 PostCard 记录:
src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { createClient } from './generated/client';

const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
  if (previousVersion) return; // fresh installs only

  const client = createClient();
  await client.postCard.create({
    data: { title: 'Welcome to Postcard', content: 'Your first card!' },
  });
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Seeds a welcome post card after install.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  handler,
});
当迁移可能破坏或损坏现有数据时,请使用 pre-install 由于安装前在先前架构上运行,且其失败会回滚升级,因此凡是有风险的操作都应放在这里:
  • 备份即将被删除或重构的数据——例如,你在 v2 中移除某个字段,需要在迁移运行前将其值复制到另一个字段或导出到存储中。
  • 归档会被新约束判为无效的记录——例如某个字段将变为 NOT NULL,你需要先删除或修正具有空值的行。
  • 验证兼容性;若当前数据无法干净迁移则拒绝升级——从处理程序中抛出异常,安装将中止且不会应用任何更改。 这比在迁移中途才发现不兼容要更安全。
  • 在会导致关联丢失的架构更改之前对数据进行重命名或重新设置键。
示例——在破坏性迁移之前归档记录:
src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { createClient } from './generated/client';

const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
  // Only the 1.x → 2.x upgrade drops the legacy `notes` field.
  if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
    return;
  }

  const client = createClient();
  const legacyRecords = await client.postCard.findMany({
    where: { notes: { isNotNull: true } },
  });

  if (legacyRecords.length === 0) return;

  // Copy legacy `notes` into the new `description` field before the migration
  // drops the `notes` column. If this fails, the upgrade is aborted and the
  // workspace stays on v1 with all data intact.
  await Promise.all(
    legacyRecords.map((record) =>
      client.postCard.update({
        where: { id: record.id },
        data: { description: record.notes },
      }),
    ),
  );
};

export default definePreInstallLogicFunction({
  universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
  name: 'pre-install',
  description: 'Backs up legacy notes into description before the v2 migration.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: true,
  handler,
});
经验法则:
你想要…使用
预填充默认数据、配置工作区、注册外部资源post-install
运行不应阻塞安装响应的长时间预填充或第三方调用post-install (默认 — shouldRunSynchronously: false,由工作线程重试)
运行安装调用返回后调用方将立即依赖的快速设置post-install,配合 shouldRunSynchronously: true
读取或备份即将被迁移丢失的数据pre-install
拒绝会损坏现有数据的升级pre-install(从处理程序中抛出异常)
在每次升级时执行对账post-install 配合 shouldRunOnVersionUpgrade: true
仅在首次安装时执行一次性设置post-install 配合 shouldRunOnVersionUpgrade: false(默认)
如有不确定,默认选择安装后(post-install)。 仅当迁移本身具有破坏性,且你需要在其丢失之前拦截先前状态时,才使用安装前。