---
title: 安装钩子
description: 在安装之前或之后运行逻辑——预置数据、备份记录、验证升级。
icon: wrench
---

安装钩子是在安装或升级生命周期期间运行的特殊逻辑函数。 它们与常规的[逻辑函数](/l/zh/developers/extend/apps/logic/logic-functions)共享相同的处理程序运行时，并接收一个 `InstallPayload`（`{ previousVersion?: string; newVersion: string }`——在全新安装时 `previousVersion` 为 `undefined`），但它们使用自己的 define 函数声明，并且存在于普通触发模型（HTTP、cron、数据库事件）之外。

每个应用**最多只能定义一个安装前函数**和**最多一个安装后函数**。 如果检测到任一类型多于一个，清单构建将报错。

```
┌─────────────────────────────────────────────────────────────┐
│ install flow                                                │
│                                                             │
│   upload package → [pre-install] → metadata migration →     │
│   generate SDK → [post-install]                             │
│                                                             │
│                  old schema visible    new schema visible   │
└─────────────────────────────────────────────────────────────┘
```

## 一览

|      | `definePreInstallLogicFunction` | `definePostInstallLogicFunction`                          |
| ---- | ------------------------------- | --------------------------------------------------------- |
| 运行   | 元数据迁移之前——**先前**的模式和数据仍然完好无损     | 迁移和 SDK 生成之后——**新的**模式已就位                                 |
| 执行   | 始终为同步；会阻塞安装                     | 默认异步（排队，重试 3 次）；可通过 `shouldRunSynchronously: true` 选择同步   |
| 失败时  | 安装在任何模式更改之前被**中止**              | 异步：最多重试 3 次。 同步：调用方会收到 `POST_INSTALL_ERROR`（模式更改**不会**回滚） |
| 典型用途 | 备份或修复迁移会丢失的数据；通过抛出异常拒绝存在风险的升级   | 预填充默认数据、配置工作区、注册外部资源                                      |

**经验法则：** 默认使用 post-install。 仅当迁移本身具有破坏性，且你需要在其丢失之前拦截先前状态时，才使用安装前。

| 你想要...             | 使用                                               |
| ------------------ | ------------------------------------------------ |
| 预填充数据、配置工作区、注册外部资源 | `post-install`                                   |
| 不应阻塞安装响应的长时间运行任务   | `post-install`（默认异步模式，带工作线程重试）                   |
| 安装返回后调用方会立即依赖的快速设置 | `post-install`，配合 `shouldRunSynchronously: true` |
| 读取或备份即将被迁移丢失的数据    | `pre-install`                                    |
| 拒绝会损坏现有数据的升级       | `pre-install`（从处理程序中抛出异常）                        |
| 在每次升级时执行对账         | 任一钩子配合 `shouldRunOnVersionUpgrade: true`         |

## 两个钩子共享的行为

* 该配置等同于 `defineLogicFunction` 的配置减去触发器设置，再加上 `shouldRunOnVersionUpgrade`。
* **运行时机**：默认情况下，仅在全新安装时运行。 将 `shouldRunOnVersionUpgrade: true` 设为 true 以便在升级时也运行。 使用 `previousVersion` / `newVersion` 按升级路径分支处理。
* **幂等性很重要**：异步 post-install 可能会被重试，而且当开启 `shouldRunOnVersionUpgrade` 时，任一钩子都会在升级时重新运行。
* 会注入常规的逻辑函数环境（`APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL`），因此你可以使用应用的令牌调用 Twenty API。
* 该钩子会在构建时自动附加到应用清单上（`preInstallLogicFunction` / `postInstallLogicFunction`）——在 [`defineApplication()`](/l/zh/developers/extend/apps/config/application) 中无需额外引用。
* 默认的 `timeoutSeconds` 为 300，以便支持更长的设置任务，例如数据填充。
* **在开发模式下不会执行**：`yarn twenty dev` 会跳过安装流程并直接同步文件，因此钩子在其中不会运行。 改为手动触发它们：

```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```

<AccordionGroup>
<Accordion title="definePostInstallLogicFunction" description="在应用工作区元数据迁移之后运行">

在应用完成安装后运行：元数据已同步、SDK 客户端已生成、新模式可被查询。 示例——在全新安装时预填充一个默认记录：

```ts src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

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

  const client = new CoreApiClient();
  await client.mutation({
    createPostCard: {
      __args: { data: { name: 'Welcome to Postcard', content: 'Your first card!' } },
      id: true,
    },
  });
};

export default definePostInstallLogicFunction({
  universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
  name: 'post-install',
  description: 'Seeds a welcome post card after install.',
  timeoutSeconds: 300,
  shouldRunOnVersionUpgrade: false,
  shouldRunSynchronously: false,
  handler,
});
```

`shouldRunSynchronously` 标志控制执行模型：

* `false` *（默认）*——放入消息队列（`retryLimit: 3`）并由工作线程运行。 安装响应会在任务被放入队列后立即返回。 **用于长时间运行的任务**——例如预填充大型数据集、调用缓慢的第三方 API。
* `true`——在安装流程中内联执行。 安装请求会阻塞直至处理程序完成；抛出的错误会以 `POST_INSTALL_ERROR` 的形式暴露给调用方（不重试）。 **用于必须在返回响应前完成的快速任务。** 此时迁移已应用，因此失败不会回滚模式更改——只会将错误暴露出来。

</Accordion>
<Accordion title="definePreInstallLogicFunction" description="在应用工作区元数据迁移之前运行">

在元数据迁移之前、针对**先前**模式运行——适合在迁移会删除数据前对其进行备份，或拒绝存在风险的升级。 在执行之前，服务器会运行一次纯增量的“精简同步”，仅注册新版本的 pre-install 函数；当你的处理程序运行时，其他一切——上一版本的对象、字段和数据——都不会被触及。

安装前始终为**同步**，并会阻塞安装。 如果处理程序抛出异常，安装会在任何模式更改之前被中止——工作区将保持在上一版本且处于一致状态。 这是有意为之：安装前是你拒绝高风险升级的最后机会。

示例——在迁移删除旧字段之前复制该旧字段的值：

```ts src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

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 = new CoreApiClient();
  const { postCards } = await client.query({
    postCards: {
      __args: { filter: { notes: { isNot: null } } },
      edges: { node: { id: true, notes: true } },
    },
  });

  // Copy legacy `notes` into `description` before the migration drops the
  // column. If this fails, the upgrade aborts and the workspace stays on v1.
  for (const { node } of postCards.edges) {
    await client.mutation({
      updatePostCard: {
        __args: { id: node.id, data: { description: node.notes } },
        id: true,
      },
    });
  }
};

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,
});
```

</Accordion>
</AccordionGroup>
