---
title: インストールフック
description: インストール、アップグレード、アンインストールのライフサイクル中にロジックを実行し、データのシード、レコードのバックアップ、アップグレードの検証、外部リソースのクリーンアップを行います。
icon: wrench
---

インストールフックは、インストール、アップグレード、またはアンインストールのライフサイクル中に実行される特別なロジック関数です。 これらは通常の[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)と同じハンドラーランタイムを共有しますが、独自の define 関数で宣言され、通常のトリガーモデル (HTTP、cron、データベースイベント) の外側で動作します。 インストールフックは `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — 新規インストールでは `previousVersion` は `undefined`) を受け取り、アンインストールフックは `UninstallPayload` (`{ version?: string }` — 削除されるバージョン) を受け取ります。

各アプリは、各フック (プレインストール、ポストインストール、アンインストール) を **最大 1 つまで** 定義できます。 いずれかが複数検出された場合、マニフェストのビルドはエラーになります。

```
┌─────────────────────────────────────────────────────────────┐
│ 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 を使用する。 マイグレーション自体が破壊的で、消える前の状態を先に扱う必要がある場合にのみ、pre-install を使ってください。

| やりたいこと…                                  | 使用                                                 |
| ---------------------------------------- | -------------------------------------------------- |
| データのシーディング、ワークスペースの構成、外部リソースの登録          | `post-install`                                     |
| インストール応答をブロックすべきでない長時間処理を実行する            | `post-install`（既定の非同期モード。ワーカーによる再試行あり）             |
| インストールが返った直後に呼び出し元がすぐに依存する高速なセットアップを実行する | `post-install`（`shouldRunSynchronously: true` を指定） |
| 次のマイグレーションで失われるデータを読み取る、またはバックアップする      | `pre-install`                                      |
| 既存データを破損させる恐れのあるアップグレードを拒否する             | `pre-install`（ハンドラーからスロー）                          |
| すべてのアップグレードで調整処理を実行する                    | `shouldRunOnVersionUpgrade: true` を指定したいずれかのフック    |

## 両方のフックに共通する動作

* 設定は、トリガー設定を除いた `defineLogicFunction` の設定に `shouldRunOnVersionUpgrade` を加えたものです。
* **実行タイミング**: 既定では新規インストール時のみ。 アップグレード時にも実行するには、`shouldRunOnVersionUpgrade: true` を設定します。 `previousVersion` / `newVersion` を使って、アップグレードパスに応じて分岐させます。
* **べき等性が重要です**: 非同期 post-install は再試行される可能性があり、さらに `shouldRunOnVersionUpgrade` が有効な場合はいずれのフックもアップグレード時に再実行されます。
* 通常のロジック関数の環境（`APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL`）が注入されるため、アプリのトークンを使って Twenty API を呼び出せます。
* フックはビルド時に自動的にアプリケーションマニフェストにアタッチされます（`preInstallLogicFunction` / `postInstallLogicFunction`）。[`defineApplication()`](/l/ja/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="ワークスペースのメタデータマイグレーションが適用される前に実行されます">

メタデータマイグレーションの前、**以前の**スキーマに対して実行されます — マイグレーションで失われるデータをバックアップしたり、リスクの高いアップグレードを拒否したりするのに適した場所です。 実行前に、サーバーは純粋に追加のみの「簡易同期」を実行し、新しいバージョンのプレインストール関数だけを登録します。あなたのハンドラーが実行される際には、それ以外 — 以前のバージョンのオブジェクト、フィールド、データ — には一切手を触れません。

プレインストールは常に**同期的**であり、インストールをブロックします。 ハンドラーがスローした場合、スキーマ変更が行われる前にインストールは中止され、ワークスペースは一貫した状態のまま前のバージョンに留まります。 これは意図的な設計です。プレインストールは、リスクの高いアップグレードを拒否できる最後の機会です。

例 — マイグレーションでレガシーフィールドが削除される前に、その値をコピーする:

```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>

## アンインストールフック

`defineUninstallLogicFunction` は、ユーザーがアプリをアンインストールしたときに実行されるフックを宣言します。 これは、アプリのメタデータ、データ、コードが削除される **前に** 実行されます。削除マイグレーションが実行された後には、実行できるものは何も残らないため、ハンドラーはまだアプリのオブジェクトやレコードをクエリできます。 これを、外部リソースのクリーンアップに使用します。API リソースのプロビジョニング解除、残っているボットの削除、webhook の失効などです。

注記：

* このフックはベストエフォートで動作します。同期的に実行されますが、失敗してもログに記録されるだけであり、**アンインストールをブロックすることは決してありません**。クリーンアップ処理が原因でアプリを削除できなくなってはなりません。
* これは `UninstallPayload` (`{ version?: string }` — 削除されるバージョン) を受け取ります。
* 新規インストールの失敗によりロールバックされた場合には実行されません。アプリのインストールが最後まで完了していないためです。
* このフックは、アプリが削除された後には実行できないため、アプリデータ (レコードに保存されたボット ID など) に依存する外部クリーンアップ処理は、外部のスケジュールジョブではなく、ここに実装する必要があります。
* インストールフックと同様に、これは **dev モードでは実行されません**。代わりに手動でトリガーしてください:

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

```ts src/logic-functions/uninstall.ts
import { defineUninstallLogicFunction, type UninstallPayload } from 'twenty-sdk/define';
import { CoreApiClient } from 'twenty-client-sdk/core';

const handler = async (_payload: UninstallPayload): Promise<void> => {
  const client = new CoreApiClient();
  const { meetingBots } = await client.query({
    meetingBots: { edges: { node: { id: true, externalBotId: true } } },
  });

  // Delete the provider-side bots so nothing keeps recording after uninstall.
  for (const { node } of meetingBots.edges) {
    await fetch(`https://api.recorder.example/bots/${node.externalBotId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.RECORDER_API_KEY}` },
    });
  }
};

export default defineUninstallLogicFunction({
  universalIdentifier: 'b2c3d4e5-6789-01bc-def0-234567890abc',
  name: 'uninstall',
  description: 'Deletes remaining recorder bots when the app is uninstalled.',
  timeoutSeconds: 300,
  handler,
});
```
