Add defineUninstallLogicFunction hook for applications (#23227)
This commit is contained in:
@@ -9,7 +9,7 @@ Every app must have exactly one `defineApplication` call. It declares:
|
||||
- **Identity** — universal identifier, display name, description.
|
||||
- **Permissions** — which role its logic functions and front components run under.
|
||||
- **Variables** *(optional)* — key–value pairs exposed to your code as environment variables.
|
||||
- **Pre-install / post-install hooks** *(optional)* — see [Logic Functions](/developers/extend/apps/logic/logic-functions).
|
||||
- **Pre-install / post-install / uninstall hooks** *(optional)* — see [Logic Functions](/developers/extend/apps/logic/logic-functions).
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
@@ -33,7 +33,7 @@ Notes:
|
||||
- `universalIdentifier` fields are deterministic IDs you own. Generate them once and keep them stable across syncs.
|
||||
- `applicationVariables` become environment variables for your functions and front components. In logic functions (server-side), they are available as `process.env.VARIABLE_NAME`. In front components, use `getApplicationVariable('VARIABLE_NAME')` from `twenty-sdk/front-component`. Variables marked with `isSecret: true` are only injected into logic functions. Front components receive only non-secret variables.
|
||||
- The default role is detected automatically from the role file marked with [`defineApplicationRole()`](/developers/extend/apps/config/roles) — you do not need to reference it from `defineApplication()`.
|
||||
- Pre-install and post-install functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
- Pre-install, post-install, and uninstall functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
- Passing `defaultRoleUniversalIdentifier` explicitly is still supported for backward compatibility, but is deprecated in favor of `defineApplicationRole()`.
|
||||
- `serverVariables` are instance-scoped configuration and secrets (e.g. API keys). Unlike `applicationVariables`, they declare no value in the manifest — the workspace operator fills them in from the app's settings, and they are injected into logic functions only once set.
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Install Hooks
|
||||
description: Run logic before or after the install — seed data, back up records, validate the upgrade.
|
||||
description: Run logic during the install, upgrade, or uninstall lifecycle — seed data, back up records, validate the upgrade, clean up external resources.
|
||||
icon: "wrench"
|
||||
---
|
||||
|
||||
Install hooks are special logic functions that run during the install or upgrade lifecycle. They share the same handler runtime as regular [logic functions](/developers/extend/apps/logic/logic-functions) and receive an `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` is `undefined` on a fresh install), but they're declared with their own define functions and live outside the normal trigger model (HTTP, cron, database events).
|
||||
Install hooks are special logic functions that run during the install, upgrade, or uninstall lifecycle. They share the same handler runtime as regular [logic functions](/developers/extend/apps/logic/logic-functions), but they're declared with their own define functions and live outside the normal trigger model (HTTP, cron, database events). Install hooks receive an `InstallPayload` (`{ previousVersion?: string; newVersion: string }` — `previousVersion` is `undefined` on a fresh install); the uninstall hook receives an `UninstallPayload` (`{ version?: string }` — the version being removed).
|
||||
|
||||
Each app may define **at most one pre-install** and **at most one post-install** function. The manifest build errors if more than one of either is detected.
|
||||
Each app may define **at most one** of each hook (pre-install, post-install, uninstall). The manifest build errors if more than one of any kind is detected.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -142,3 +142,47 @@ export default definePreInstallLogicFunction({
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Uninstall hook
|
||||
|
||||
`defineUninstallLogicFunction` declares a hook that runs when a user uninstalls your app. It executes **before** the app's metadata, data, and code are removed — once the deletion migration runs there is nothing left to execute — so your handler can still query the app's objects and records. Use it for cleanup of external resources: deprovision API resources, delete remaining bots, revoke webhooks.
|
||||
|
||||
Notes:
|
||||
|
||||
- The hook is best-effort: it runs synchronously, but a failure is logged and **never blocks the uninstall** — cleanup must not make an app impossible to remove.
|
||||
- It receives `UninstallPayload` (`{ version?: string }` — the version being removed).
|
||||
- It does **not** run when a failed fresh install is rolled back — the app never finished installing.
|
||||
- The hook cannot run after the app is gone, so external cleanup that depends on app data (e.g. bot IDs stored in records) belongs here, not in an external scheduled job.
|
||||
- Like the install hooks, it is **not executed in dev mode** — trigger it manually instead:
|
||||
|
||||
```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,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -23,6 +23,11 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
└──────────────────────────────────┘
|
||||
┌──────────────────────────────────┐
|
||||
│ Post-install hook │ after metadata migration
|
||||
└──────────────────────────────────┘
|
||||
│
|
||||
▼ (at uninstall time)
|
||||
┌──────────────────────────────────┐
|
||||
│ Uninstall hook │ before app removal
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -36,7 +41,7 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
`defineRole` — declare what your app's logic functions can read and write.
|
||||
</Card>
|
||||
<Card title="Install Hooks" icon="wrench" href="/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` and `definePostInstallLogicFunction` — back up data, seed defaults, validate upgrades.
|
||||
`definePreInstallLogicFunction`, `definePostInstallLogicFunction`, and `defineUninstallLogicFunction` — back up data, seed defaults, validate upgrades, clean up on removal.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -44,7 +49,7 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
|
||||
- **Application** is the entry point. Every app has exactly one `defineApplication()` call, and it points at one **Role** as its default.
|
||||
- The **Role** controls what the app's logic functions and front components can read and write. Follow least-privilege: only grant the permissions your code actually needs.
|
||||
- **Install Hooks** run during install or upgrade — pre-install before the metadata migration (so it can refuse a risky upgrade), post-install after the migration (so it can seed default data against the new schema).
|
||||
- **Install Hooks** run during install or upgrade — pre-install before the metadata migration (so it can refuse a risky upgrade), post-install after the migration (so it can seed default data against the new schema). The uninstall hook runs right before the app is removed, so it can clean up external resources while the app's data is still readable.
|
||||
|
||||
<Note>
|
||||
Install hooks share the [logic function](/developers/extend/apps/logic/logic-functions) runtime — same handler signature, same environment variables, same typed API client — but they're declared with their own define functions and live outside the regular trigger model (HTTP, cron, database events).
|
||||
|
||||
@@ -69,7 +69,7 @@ your-app/
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Install flow │
|
||||
│ upload → [pre-install] → metadata migration → │
|
||||
│ generate SDK → [post-install] │
|
||||
│ generate SDK → [post-install] → … → [uninstall] │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Publish │
|
||||
│ npm publish → appears in Twenty marketplace │
|
||||
@@ -78,13 +78,13 @@ your-app/
|
||||
|
||||
- **`yarn twenty dev`** — watches your source files and live-syncs changes to a connected Twenty server. The typed API client is regenerated automatically when the schema changes.
|
||||
- **`yarn twenty dev:build`** — compiles TypeScript, bundles logic functions and front components with esbuild, and produces a manifest.
|
||||
- **Pre/post-install hooks** — optional functions that run during installation. See [Install Hooks](/developers/extend/apps/config/install-hooks) for details.
|
||||
- **Pre/post-install and uninstall hooks** — optional functions that run during installation or right before removal. See [Install Hooks](/developers/extend/apps/config/install-hooks) for details.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/developers/extend/apps/config/overview">
|
||||
Application identity, default role, and install hooks.
|
||||
Application identity, default role, and install and uninstall hooks.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
|
||||
@@ -149,7 +149,7 @@ Full reference: [Concepts](/developers/extend/apps/getting-started/concepts).
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/developers/extend/apps/config/overview">
|
||||
Application identity, default role, install hooks, public assets.
|
||||
Application identity, default role, install and uninstall hooks, public assets.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
|
||||
@@ -612,7 +612,7 @@ const handler = async (params: { parentMessageId?: string }) => {
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**Install hooks** — pre-install and post-install handlers — share this runtime but are declared with their own define functions and don't take trigger settings. See [Install Hooks](/developers/extend/apps/config/install-hooks) for `definePreInstallLogicFunction` and `definePostInstallLogicFunction`.
|
||||
**Install hooks** — pre-install, post-install, and uninstall handlers — share this runtime but are declared with their own define functions and don't take trigger settings. See [Install Hooks](/developers/extend/apps/config/install-hooks) for `definePreInstallLogicFunction`, `definePostInstallLogicFunction`, and `defineUninstallLogicFunction`.
|
||||
</Note>
|
||||
|
||||
## Typed API clients (twenty-client-sdk)
|
||||
|
||||
@@ -38,9 +38,10 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
# Pass a JSON payload
|
||||
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
|
||||
# Execute the install hooks
|
||||
# Execute the install and uninstall hooks
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
yarn twenty dev:function:exec --preInstall
|
||||
yarn twenty dev:function:exec --uninstall
|
||||
```
|
||||
|
||||
## Viewing function logs (`yarn twenty dev:function:logs`)
|
||||
|
||||
Reference in New Issue
Block a user