---
title: Installations-Hooks
description: Führen Sie Logik während des Installations-, Upgrade- oder Deinstallations-Lebenszyklus aus – initialisieren Sie Daten, sichern Sie Datensätze, validieren Sie das Upgrade, bereinigen Sie externe Ressourcen.
icon: wrench
---
Installations-Hooks sind spezielle Logikfunktionen, die während des Installations-, Upgrade- oder Deinstallations-Lebenszyklus ausgeführt werden. Sie verwenden dieselbe Handler-Laufzeit wie reguläre [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions), werden jedoch mit eigenen Define-Funktionen deklariert und sind vom normalen Trigger-Modell (HTTP, Cron, Datenbankereignisse) getrennt. Installations-Hooks erhalten ein `InstallPayload` (`{ previousVersion?: string; newVersion: string }` – `previousVersion` ist bei einer Neuinstallation `undefined`); der Deinstallations-Hook erhält ein `UninstallPayload` (`{ version?: string }` – die entfernte Version).
Jede App darf **höchstens einen** Hook jeder Art definieren (Pre-Install, Post-Install, Uninstall). Der Manifest-Build schlägt fehl, wenn mehr als ein Hook eines Typs erkannt wird.
```
┌─────────────────────────────────────────────────────────────┐
│ install flow │
│ │
│ upload package → [pre-install] → metadata migration → │
│ generate SDK → [post-install] │
│ │
│ old schema visible new schema visible │
└─────────────────────────────────────────────────────────────┘
```
## Auf einen Blick
| | `definePreInstallLogicFunction` | `definePostInstallLogicFunction` |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Zeitpunkt der Ausführung | Vor der Metadatenmigration — das **bisherige** Schema und die Daten sind noch intakt | Nach der Migration und SDK-Generierung — das **neue** Schema ist aktiv |
| Ausführung | Immer synchron; blockiert die Installation | Standardmäßig asynchron (warteschlangengesteuert, 3 Wiederholungsversuche); synchrones Opt-in über `shouldRunSynchronously: true` |
| Im Fehlerfall | Die Installation wird **abgebrochen**, bevor eine Schemaänderung erfolgt | Asynchron: bis zu 3 Mal erneut ausgeführt. Synchron: Der Aufrufer erhält `POST_INSTALL_ERROR` (Schemaänderungen werden **nicht** zurückgerollt) |
| Typische Verwendung | Daten sichern oder korrigieren, die eine Migration verlieren würde; ein riskantes Upgrade ablehnen, indem ein Fehler geworfen wird | Standarddaten befüllen, den Arbeitsbereich konfigurieren, externe Ressourcen registrieren |
**Faustregel:** Standardmäßig Post-Install verwenden. Greifen Sie nur zu Pre-Install, wenn die Migration selbst destruktiv ist und Sie den vorherigen Zustand abfangen müssen, bevor er verloren geht.
| Sie möchten ... | Verwenden |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Daten befüllen, den Arbeitsbereich konfigurieren, externe Ressourcen registrieren | `post-install` |
| Lange laufende Aufgaben, die die Installationsantwort nicht blockieren sollten | `post-install` (standardmäßig asynchroner Modus, mit Worker-Wiederholungsversuchen) |
| Schnelle Einrichtung, auf die sich der Aufrufer unmittelbar nach der Rückkehr der Installationsantwort verlässt | `post-install` mit `shouldRunSynchronously: true` |
| Daten lesen oder sichern, die bei der bevorstehenden Migration verloren gingen | `pre-install` |
| Ein Upgrade ablehnen, das vorhandene Daten beschädigen würde | `pre-install` (`throw` im Handler) |
| Abgleich bei jedem Upgrade | Einer der Hooks mit `shouldRunOnVersionUpgrade: true` |
## Verhalten, das von beiden Hooks geteilt wird
* Die Konfiguration ist eine `defineLogicFunction`-Konfiguration ohne die Trigger-Einstellungen, aber mit `shouldRunOnVersionUpgrade`.
* **Wann sie ausgeführt werden**: standardmäßig nur bei Neuinstallationen. Setze `shouldRunOnVersionUpgrade: true`, um sie auch bei Upgrades auszuführen. Verwende `previousVersion` / `newVersion`, um je nach Upgrade-Pfad unterschiedlich zu verzweigen.
* **Idempotenz ist wichtig**: Asynchrones Post-Install kann erneut ausgeführt werden, und beide Hooks werden bei Upgrades erneut ausgeführt, wenn `shouldRunOnVersionUpgrade` aktiviert ist.
* Die übliche Logikfunktions-Umgebung (`APPLICATION_ID`, `APP_ACCESS_TOKEN`, `API_URL`) wird injiziert, sodass du die Twenty-API mit dem Token deiner App aufrufen kannst.
* Der Hook wird zur Build-Zeit automatisch an das Anwendungsmanifest angehängt (`preInstallLogicFunction` / `postInstallLogicFunction`) — in [`defineApplication()`](/l/de/developers/extend/apps/config/application) muss nichts referenziert werden.
* Der Standardwert für `timeoutSeconds` ist 300, um längere Einrichtungsaufgaben wie Daten-Seeding zu ermöglichen.
* **Wird im Dev-Modus nicht ausgeführt**: `yarn twenty dev` überspringt den Installations-Flow und synchronisiert Dateien direkt, sodass Hooks dort nie ausgeführt werden. Führe sie stattdessen manuell aus:
```bash filename="Terminal"
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```
Wird ausgeführt, nachdem deine App die Installation abgeschlossen hat: Metadaten synchronisiert, SDK-Client generiert, neues Schema abfragbar. Beispiel — bei Neuinstallationen einen Standarddatensatz anlegen:
```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 => {
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,
});
```
Das Flag `shouldRunSynchronously` steuert das Ausführungsmodell:
* `false` *(Standard)* — in die Nachrichtenwarteschlange eingereiht (`retryLimit: 3`) und von einem Worker ausgeführt. Die Installationsantwort wird zurückgegeben, sobald der Job in die Warteschlange eingereiht wurde. **Für lange laufende Aufgaben verwenden** — das Befüllen großer Datensätze, langsame Drittanbieter-APIs.
* `true` — wird inline während des Installations-Flows ausgeführt. Die Installationsanforderung blockiert, bis der Handler fertig ist; ein geworfener Fehler erscheint als `POST_INSTALL_ERROR` beim Aufrufer (keine Wiederholungsversuche). **Für schnelle Aufgaben verwenden, die unbedingt vor der Antwort abgeschlossen sein müssen.** Die Migration wurde zu diesem Zeitpunkt bereits angewendet, daher werden Schemaänderungen bei einem Fehler nicht zurückgerollt — es wird nur der Fehler nach außen gegeben.
Wird vor der Metadatenmigration gegen das **bisherige** Schema ausgeführt — die richtige Stelle, um Daten zu sichern, die eine Migration verlieren würde, oder um ein riskantes Upgrade abzulehnen. Vor der Ausführung führt der Server einen rein additiven „pared-down sync“ durch, der nur die Pre-Install-Funktion der neuen Version registriert; alles andere — Objekte, Felder und Daten der vorherigen Version — bleibt unangetastet, wenn dein Handler ausgeführt wird.
Pre-Install ist immer **synchron** und blockiert die Installation. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor eine Schemaänderung erfolgt — der Arbeitsbereich verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen.
Beispiel — die Werte eines Legacy-Feldes kopieren, bevor die Migration es entfernt:
```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 => {
// 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,
});
```
## Deinstallations-Hook
`defineUninstallLogicFunction` deklariert einen Hook, der ausgeführt wird, wenn ein Benutzer Ihre App deinstalliert. Er wird **ausgeführt, bevor** die Metadaten, Daten und der Code der App entfernt werden – sobald die Löschmigration ausgeführt wurde, bleibt nichts mehr zum Ausführen übrig – sodass Ihr Handler weiterhin die Objekte und Datensätze der App abfragen kann. Verwenden Sie ihn zur Bereinigung externer Ressourcen: Stellen Sie API-Ressourcen außer Betrieb, löschen Sie verbleibende Bots, widerrufen Sie Webhooks.
Notizen:
* Der Hook ist Best-Effort: Er wird synchron ausgeführt, aber ein Fehler wird protokolliert und **blockiert die Deinstallation niemals** – die Bereinigung darf es nicht unmöglich machen, eine App zu entfernen.
* Er erhält `UninstallPayload` (`{ version?: string }` – die entfernte Version).
* Er wird **nicht** ausgeführt, wenn eine fehlgeschlagene Neuinstallation zurückgerollt wird – die App wurde nie vollständig installiert.
* Der Hook kann nicht ausgeführt werden, nachdem die App entfernt wurde, daher gehört externe Bereinigung, die von App-Daten abhängt (z. B. in Datensätzen gespeicherte Bot-IDs), hierher und nicht in einen externen geplanten Job.
* Wie die Installations-Hooks wird er **nicht im Dev-Modus ausgeführt** – lösen Sie ihn stattdessen manuell aus:
```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 => {
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,
});
```