i18n - docs translations (#20366)
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
committed by
GitHub
parent
24e64350ee
commit
95bc8aea28
@@ -2107,7 +2107,7 @@
|
||||
"group": "Apps",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "Erste Schritte",
|
||||
"pages": [
|
||||
"l/de/developers/extend/apps/getting-started/quick-start",
|
||||
"l/de/developers/extend/apps/getting-started/concepts",
|
||||
@@ -2118,7 +2118,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "Konfiguration",
|
||||
"pages": [
|
||||
"l/de/developers/extend/apps/config/overview",
|
||||
"l/de/developers/extend/apps/config/application",
|
||||
@@ -2128,7 +2128,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"group": "Daten",
|
||||
"pages": [
|
||||
"l/de/developers/extend/apps/data/overview",
|
||||
"l/de/developers/extend/apps/data/objects",
|
||||
@@ -2137,7 +2137,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "Logik",
|
||||
"pages": [
|
||||
"l/de/developers/extend/apps/logic/overview",
|
||||
"l/de/developers/extend/apps/logic/logic-functions",
|
||||
@@ -2157,7 +2157,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "Operationen",
|
||||
"pages": [
|
||||
"l/de/developers/extend/apps/operations/overview",
|
||||
"l/de/developers/extend/apps/operations/cli",
|
||||
@@ -4272,7 +4272,7 @@
|
||||
"group": "Aplicativos",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "Primeiros passos",
|
||||
"pages": [
|
||||
"l/pt/developers/extend/apps/getting-started/quick-start",
|
||||
"l/pt/developers/extend/apps/getting-started/concepts",
|
||||
@@ -4283,7 +4283,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "Configuração",
|
||||
"pages": [
|
||||
"l/pt/developers/extend/apps/config/overview",
|
||||
"l/pt/developers/extend/apps/config/application",
|
||||
@@ -4302,7 +4302,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "Lógica",
|
||||
"pages": [
|
||||
"l/pt/developers/extend/apps/logic/overview",
|
||||
"l/pt/developers/extend/apps/logic/logic-functions",
|
||||
@@ -4322,7 +4322,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "Operações",
|
||||
"pages": [
|
||||
"l/pt/developers/extend/apps/operations/overview",
|
||||
"l/pt/developers/extend/apps/operations/cli",
|
||||
@@ -4705,7 +4705,7 @@
|
||||
"group": "Aplicații",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "Începeți",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/getting-started/quick-start",
|
||||
"l/ro/developers/extend/apps/getting-started/concepts",
|
||||
@@ -4716,7 +4716,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "Configurare",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/config/overview",
|
||||
"l/ro/developers/extend/apps/config/application",
|
||||
@@ -4726,7 +4726,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"group": "Date",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/data/overview",
|
||||
"l/ro/developers/extend/apps/data/objects",
|
||||
@@ -4735,7 +4735,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "Logică",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/logic/overview",
|
||||
"l/ro/developers/extend/apps/logic/logic-functions",
|
||||
@@ -4744,7 +4744,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Layout",
|
||||
"group": "Aspect",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/layout/overview",
|
||||
"l/ro/developers/extend/apps/layout/views",
|
||||
@@ -4755,7 +4755,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "Operațiuni",
|
||||
"pages": [
|
||||
"l/ro/developers/extend/apps/operations/overview",
|
||||
"l/ro/developers/extend/apps/operations/cli",
|
||||
@@ -5138,7 +5138,7 @@
|
||||
"group": "Приложения",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "Начало работы",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/getting-started/quick-start",
|
||||
"l/ru/developers/extend/apps/getting-started/concepts",
|
||||
@@ -5149,7 +5149,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "Конфигурация",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/config/overview",
|
||||
"l/ru/developers/extend/apps/config/application",
|
||||
@@ -5159,7 +5159,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"group": "Данные",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/data/overview",
|
||||
"l/ru/developers/extend/apps/data/objects",
|
||||
@@ -5168,7 +5168,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "Логика",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/logic/overview",
|
||||
"l/ru/developers/extend/apps/logic/logic-functions",
|
||||
@@ -5177,7 +5177,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Layout",
|
||||
"group": "Макет",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/layout/overview",
|
||||
"l/ru/developers/extend/apps/layout/views",
|
||||
@@ -5188,7 +5188,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "Операции",
|
||||
"pages": [
|
||||
"l/ru/developers/extend/apps/operations/overview",
|
||||
"l/ru/developers/extend/apps/operations/cli",
|
||||
@@ -5571,7 +5571,7 @@
|
||||
"group": "Uygulamalar",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "Başlarken",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/getting-started/quick-start",
|
||||
"l/tr/developers/extend/apps/getting-started/concepts",
|
||||
@@ -5582,7 +5582,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "Yapılandırma",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/config/overview",
|
||||
"l/tr/developers/extend/apps/config/application",
|
||||
@@ -5592,7 +5592,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"group": "Veri",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/data/overview",
|
||||
"l/tr/developers/extend/apps/data/objects",
|
||||
@@ -5601,7 +5601,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "Mantık",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/logic/overview",
|
||||
"l/tr/developers/extend/apps/logic/logic-functions",
|
||||
@@ -5610,7 +5610,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Layout",
|
||||
"group": "Düzen",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/layout/overview",
|
||||
"l/tr/developers/extend/apps/layout/views",
|
||||
@@ -5621,7 +5621,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "İşlemler",
|
||||
"pages": [
|
||||
"l/tr/developers/extend/apps/operations/overview",
|
||||
"l/tr/developers/extend/apps/operations/cli",
|
||||
@@ -6004,7 +6004,7 @@
|
||||
"group": "应用",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"group": "开始使用",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/getting-started/quick-start",
|
||||
"l/zh/developers/extend/apps/getting-started/concepts",
|
||||
@@ -6015,7 +6015,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Config",
|
||||
"group": "配置",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/config/overview",
|
||||
"l/zh/developers/extend/apps/config/application",
|
||||
@@ -6025,7 +6025,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"group": "数据",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/data/overview",
|
||||
"l/zh/developers/extend/apps/data/objects",
|
||||
@@ -6034,7 +6034,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Logic",
|
||||
"group": "逻辑",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/logic/overview",
|
||||
"l/zh/developers/extend/apps/logic/logic-functions",
|
||||
@@ -6043,7 +6043,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Layout",
|
||||
"group": "布局",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/layout/overview",
|
||||
"l/zh/developers/extend/apps/layout/views",
|
||||
@@ -6054,7 +6054,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Operations",
|
||||
"group": "操作",
|
||||
"pages": [
|
||||
"l/zh/developers/extend/apps/operations/overview",
|
||||
"l/zh/developers/extend/apps/operations/cli",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Application Config
|
||||
description: Declare your app's identity, default role, variables, and marketplace metadata with defineApplication.
|
||||
title: App-Konfiguration
|
||||
description: Deklarieren Sie die Identität, die Standardrolle, Variablen und Marktplatz-Metadaten Ihrer App mit `defineApplication`.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
Every app must have exactly one `defineApplication` call. It declares:
|
||||
Jede App muss genau einen Aufruf von `defineApplication` haben. Dieser deklariert:
|
||||
|
||||
* **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](/l/de/developers/extend/apps/logic/logic-functions).
|
||||
* **Identität** — universeller Bezeichner, Anzeigename, Beschreibung.
|
||||
* **Berechtigungen** — unter welcher Rolle ihre Logikfunktionen und Frontend-Komponenten ausgeführt werden.
|
||||
* **Variablen** *(optional)* — Schlüssel–Wert-Paare, die Ihrem Code als Umgebungsvariablen zur Verfügung gestellt werden.
|
||||
* **Pre-install-/Post-install-Hooks** *(optional)* — siehe [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions).
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
@@ -31,35 +31,35 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
Notizen:
|
||||
|
||||
* `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 (e.g., `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` must reference a role defined with [`defineRole()`](/l/de/developers/extend/apps/config/roles).
|
||||
* Pre-install and post-install functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
* `universalIdentifier`-Felder sind deterministische IDs, die Ihnen gehören. Erzeugen Sie sie einmal und halten Sie sie über Synchronisierungen hinweg stabil.
|
||||
* `applicationVariables` werden zu Umgebungsvariablen für Ihre Funktionen und Frontend-Komponenten (z. B. ist `DEFAULT_RECIPIENT_NAME` als `process.env.DEFAULT_RECIPIENT_NAME` verfügbar).
|
||||
* `defaultRoleUniversalIdentifier` muss auf eine mit [`defineRole()`](/l/de/developers/extend/apps/config/roles) definierte Rolle verweisen.
|
||||
* Pre- und Post-Installationsfunktionen werden während des Manifest-Builds automatisch erkannt — Sie müssen sie in `defineApplication()` nicht referenzieren.
|
||||
|
||||
## Default function role
|
||||
## Standard-Funktionsrolle
|
||||
|
||||
The `defaultRoleUniversalIdentifier` controls what the app's logic functions and front components can access:
|
||||
Der `defaultRoleUniversalIdentifier` steuert, worauf die Logikfunktionen und Frontend-Komponenten der App zugreifen können:
|
||||
|
||||
* The runtime token injected as `TWENTY_APP_ACCESS_TOKEN` is derived from this role.
|
||||
* The typed API client is restricted to the permissions granted to that role.
|
||||
* Follow least-privilege: declare only the permissions your functions need.
|
||||
* Das zur Laufzeit als `TWENTY_APP_ACCESS_TOKEN` injizierte Token wird aus dieser Rolle abgeleitet.
|
||||
* Der typisierte API-Client ist auf die dieser Rolle gewährten Berechtigungen beschränkt.
|
||||
* Befolgen Sie das Least-Privilege-Prinzip: Deklarieren Sie nur die Berechtigungen, die Ihre Funktionen benötigen.
|
||||
|
||||
When you scaffold a new app, the CLI creates a starter role file at `src/roles/default-role.ts`. See [Roles & Permissions](/l/de/developers/extend/apps/config/roles) for the full reference.
|
||||
Wenn Sie eine neue App erzeugen, erstellt die CLI eine Starter-Rolldatei unter `src/roles/default-role.ts`. Die vollständige Referenz finden Sie unter [Rollen & Berechtigungen](/l/de/developers/extend/apps/config/roles).
|
||||
|
||||
## Marketplace metadata
|
||||
## Marktplatz-Metadaten
|
||||
|
||||
If you plan to [publish your app](/l/de/developers/extend/apps/operations/publishing), these optional fields control how it appears in the marketplace:
|
||||
Wenn Sie planen, [Ihre App zu veröffentlichen](/l/de/developers/extend/apps/operations/publishing), steuern diese optionalen Felder, wie Ihre App im Marktplatz erscheint:
|
||||
|
||||
| Field | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Author or company name |
|
||||
| `category` | App category for marketplace filtering |
|
||||
| `logoUrl` | Path to your app logo (e.g., `public/logo.png`) |
|
||||
| `screenshots` | Array of screenshot paths (e.g., `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm |
|
||||
| `websiteUrl` | Link to your website |
|
||||
| `termsUrl` | Link to terms of service |
|
||||
| `emailSupport` | Support email address |
|
||||
| `issueReportUrl` | Link to issue tracker |
|
||||
| Feld | Beschreibung |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Name des Autors oder des Unternehmens |
|
||||
| `category` | App-Kategorie für die Filterung im Marktplatz |
|
||||
| `logoUrl` | Pfad zu Ihrem App-Logo (z. B. `public/logo.png`) |
|
||||
| `screenshots` | Array von Screenshot-Pfaden (z. B. `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Längere Markdown-Beschreibung für den Tab "Info". Wenn weggelassen, verwendet der Marktplatz die `README.md` des Pakets von npm |
|
||||
| `websiteUrl` | Link zu Ihrer Website |
|
||||
| `termsUrl` | Link zu den Nutzungsbedingungen |
|
||||
| `emailSupport` | Support-E-Mail-Adresse |
|
||||
| `issueReportUrl` | Link zum Issue-Tracker |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Install Hooks
|
||||
description: Run logic before or after the install — seed data, back up records, validate the upgrade.
|
||||
icon: wrench
|
||||
title: Installations-Hooks
|
||||
description: Führen Sie Logik vor oder nach der Installation aus – befüllen Sie Daten, sichern Sie Datensätze und validieren Sie das Upgrade.
|
||||
icon: Schraubenschlüssel
|
||||
---
|
||||
|
||||
Install hooks are special logic functions that run during the install or upgrade lifecycle. They share the same handler runtime as regular [logic functions](/l/de/developers/extend/apps/logic/logic-functions) and receive an `InstallPayload`, but they're declared with their own define functions — `definePostInstallLogicFunction()` and `definePreInstallLogicFunction()` — and live outside the normal trigger model (HTTP, cron, database events).
|
||||
Installations-Hooks sind spezielle Logikfunktionen, die während des Installations- oder Upgrade-Lebenszyklus ausgeführt werden. Sie verwenden dieselbe Handler-Laufzeit wie reguläre [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions) und erhalten ein `InstallPayload`, werden jedoch mit eigenen Define-Funktionen deklariert – `definePostInstallLogicFunction()` und `definePreInstallLogicFunction()` – und sind vom normalen Trigger-Modell (HTTP, Cron, Datenbankereignisse) getrennt.
|
||||
|
||||
Each app may define **at most one pre-install** and **at most one post-install** function. The manifest build will error if more than one of either is detected.
|
||||
Jede App darf **höchstens eine Pre-Install-Funktion** und **höchstens eine Post-Install-Funktion** definieren. Der Manifest-Build schlägt fehl, wenn mehr als eine von beiden erkannt wird.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -20,9 +20,9 @@ Each app may define **at most one pre-install** and **at most one post-install**
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Runs after the workspace metadata migration is applied">
|
||||
<Accordion title="definePostInstallLogicFunction" description="Wird ausgeführt, nachdem die Workspace-Metadatenmigration angewendet wurde">
|
||||
|
||||
A post-install function runs automatically once your app has finished installing on a workspace. The server executes it **after** the app's metadata has been synchronized and the SDK client has been generated, so the workspace is fully ready to use and the new schema is in place. Typical use cases include seeding default data, creating initial records, configuring workspace settings, or provisioning resources on third-party services.
|
||||
Eine Post-Install-Funktion wird automatisch ausgeführt, sobald Ihre App die Installation in einem Workspace abgeschlossen hat. Der Server führt sie **nach** der Synchronisierung der Metadaten der App und der Generierung des SDK-Clients aus, sodass der Arbeitsbereich vollständig einsatzbereit ist und das neue Schema bereitsteht. Typische Anwendungsfälle umfassen das Befüllen von Standarddaten, das Erstellen anfänglicher Datensätze, das Konfigurieren von Arbeitsbereichseinstellungen oder das Bereitstellen von Ressourcen bei Diensten von Drittanbietern.
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -42,30 +42,30 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the post-install function at any time using the CLI:
|
||||
Sie können die Post-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Post-install functions use `definePostInstallLogicFunction()` — a specialized variant that omits trigger settings (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* The handler receives an `InstallPayload` with `{ previousVersion?: string; newVersion: string }` — `newVersion` is the version being installed, and `previousVersion` is the version that was previously installed (or `undefined` on a fresh install). Use these values to distinguish fresh installs from upgrades and to run version-specific migration logic.
|
||||
* **When the hook runs**: on fresh installs only, by default. Pass `shouldRunOnVersionUpgrade: true` if you also want it to run when the app is upgraded from a previous version. When omitted, the flag defaults to `false` and upgrades skip the hook.
|
||||
* **Execution model — async by default, sync opt-in**: the `shouldRunSynchronously` flag controls *how* post-install is executed.
|
||||
* `shouldRunSynchronously: false` *(default)* — the hook is **enqueued on the message queue** with `retryLimit: 3` and runs asynchronously in a worker. The install response returns as soon as the job is enqueued, so a slow or failing handler does not block the caller. The worker will retry up to three times. **Use this for long-running jobs** — seeding large datasets, calling slow third-party APIs, provisioning external resources, anything that might exceed a reasonable HTTP response window.
|
||||
* `shouldRunSynchronously: true` — the hook is executed **inline during the install flow** (same executor as pre-install). The install request blocks until the handler finishes, and if it throws, the install caller receives a `POST_INSTALL_ERROR`. No automatic retries. **Use this for fast, must-complete-before-response work** — for example, emitting a validation error to the user, or quick setup that the client will rely on immediately after the install call returns. Keep in mind the metadata migration has already been applied by the time post-install runs, so a sync-mode failure does **not** roll back the schema changes — it only surfaces the error.
|
||||
* Make sure your handler is idempotent. In async mode the queue may retry up to three times; in either mode the hook may run again on upgrades when `shouldRunOnVersionUpgrade: true`.
|
||||
* The environment variables `APPLICATION_ID`, `APP_ACCESS_TOKEN`, and `API_URL` are available inside the handler (same as any other logic function), so you can call the Twenty API with an application access token scoped to your app.
|
||||
* Only one post-install function is allowed per application. The manifest build will error if more than one is detected.
|
||||
* The function's `universalIdentifier`, `shouldRunOnVersionUpgrade`, and `shouldRunSynchronously` are automatically attached to the application manifest under the `postInstallLogicFunction` field during the build — you do not need to reference them in [`defineApplication()`](/l/de/developers/extend/apps/config/application).
|
||||
* The default timeout is set to 300 seconds (5 minutes) to allow for longer setup tasks like data seeding.
|
||||
* **Not executed in dev mode**: when an app is registered locally (via `yarn twenty dev`), the server skips the install flow entirely and syncs files directly through the CLI watcher — so post-install never runs in dev mode, regardless of `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` to trigger it manually against a running workspace.
|
||||
Hauptpunkte:
|
||||
* Post-Installationsfunktionen verwenden `definePostInstallLogicFunction()` — eine spezialisierte Variante, die Trigger-Einstellungen (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`) weglässt.
|
||||
* Der Handler erhält ein `InstallPayload` mit `{ previousVersion?: string; newVersion: string }` — `newVersion` ist die zu installierende Version, und `previousVersion` ist die zuvor installierte Version (oder `undefined` bei einer Neuinstallation). Verwenden Sie diese Werte, um Neuinstallationen von Upgrades zu unterscheiden und versionsspezifische Migrationslogik auszuführen.
|
||||
* **Wann der Hook ausgeführt wird**: standardmäßig nur bei Neuinstallationen. Übergeben Sie `shouldRunOnVersionUpgrade: true`, wenn er auch beim Upgrade der App von einer vorherigen Version ausgeführt werden soll. Wenn weggelassen, ist das Flag standardmäßig `false` und Upgrades überspringen den Hook.
|
||||
* **Ausführungsmodell — standardmäßig asynchron, synchron optional**: Das Flag `shouldRunSynchronously` steuert, *wie* Post-Install ausgeführt wird.
|
||||
* `shouldRunSynchronously: false` *(Standard)* — der Hook wird **in die Nachrichtenwarteschlange eingereiht** mit `retryLimit: 3` und läuft asynchron in einem Worker. Die Installationsantwort kommt zurück, sobald der Job eingereiht ist, sodass ein langsamer oder fehlschlagender Handler den Aufrufer nicht blockiert. Der Worker versucht es bis zu dreimal erneut. **Verwenden Sie dies für lang laufende Jobs** — das Befüllen großer Datensätze, Aufrufe langsamer Drittanbieter-APIs, Bereitstellung externer Ressourcen, alles, was ein vernünftiges HTTP-Antwortfenster überschreiten könnte.
|
||||
* `shouldRunSynchronously: true` — der Hook wird **inline während des Installationsablaufs** ausgeführt (gleicher Executor wie bei Pre-Install). Die Installationsanforderung blockiert, bis der Handler fertig ist, und wenn er einen Fehler wirft, erhält der Installationsaufrufer einen `POST_INSTALL_ERROR`. Keine automatischen Wiederholungen. **Verwenden Sie dies für schnelle Aufgaben, die vor der Antwort abgeschlossen sein müssen** — z. B. um dem Benutzer einen Validierungsfehler auszugeben oder für eine schnelle Einrichtung, auf die der Client unmittelbar nach der Rückkehr des Installationsaufrufs angewiesen ist. Beachten Sie, dass die Metadatenmigration bereits angewendet wurde, wenn Post-Install läuft, sodass ein Fehler im Synchronmodus die Schemaänderungen **nicht** rückgängig macht — er zeigt lediglich den Fehler an.
|
||||
* Stellen Sie sicher, dass Ihr Handler idempotent ist. Im asynchronen Modus kann die Warteschlange bis zu dreimal erneut versuchen; in beiden Modi kann der Hook bei Upgrades erneut laufen, wenn `shouldRunOnVersionUpgrade: true`.
|
||||
* Die Umgebungsvariablen `APPLICATION_ID`, `APP_ACCESS_TOKEN` und `API_URL` sind im Handler verfügbar (wie bei jeder anderen Logikfunktion), sodass Sie die Twenty API mit einem auf Ihre App beschränkten Anwendungszugriffstoken aufrufen können.
|
||||
* Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
|
||||
* Die `universalIdentifier`, `shouldRunOnVersionUpgrade` und `shouldRunSynchronously` der Funktion werden während des Builds automatisch dem Anwendungsmanifest unter dem Feld `postInstallLogicFunction` hinzugefügt – Sie müssen sie in [`defineApplication()`](/l/de/developers/extend/apps/config/application) nicht referenzieren.
|
||||
* Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen.
|
||||
* **Nicht im Dev-Modus ausgeführt**: Wenn eine App lokal registriert ist (über `yarn twenty dev`), überspringt der Server den Installationsablauf vollständig und synchronisiert Dateien direkt über den CLI-Watcher — daher läuft Post-Install im Dev-Modus nie, unabhängig von `shouldRunSynchronously`. Verwenden Sie `yarn twenty exec --postInstall`, um es manuell gegen einen laufenden Workspace auszulösen.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Runs before the workspace metadata migration is applied">
|
||||
<Accordion title="definePreInstallLogicFunction" description="Wird ausgeführt, bevor die Workspace-Metadatenmigration angewendet wird">
|
||||
|
||||
A pre-install function runs automatically during installation, **before the workspace metadata migration is applied**. It shares the same payload shape as post-install (`InstallPayload`), but it is positioned earlier in the install flow so it can prepare state that the upcoming migration depends on — typical uses include backing up data, validating compatibility with the new schema, or archiving records that are about to be restructured or dropped.
|
||||
Eine Pre-Install-Funktion wird automatisch während der Installation ausgeführt, **bevor die Workspace-Metadatenmigration angewendet wird**. Sie hat die gleiche Payload-Struktur wie Post-Install (`InstallPayload`), ist aber früher im Installationsablauf positioniert, sodass sie Zustände vorbereiten kann, von denen die bevorstehende Migration abhängt — typische Anwendungsfälle sind das Sichern von Daten, die Validierung der Kompatibilität mit dem neuen Schema oder das Archivieren von Datensätzen, die umstrukturiert oder entfernt werden sollen.
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -84,35 +84,35 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the pre-install function at any time using the CLI:
|
||||
Sie können die Pre-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --preInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Pre-install functions use `definePreInstallLogicFunction()` — same specialized config as post-install, just attached to a different lifecycle slot.
|
||||
* Both pre- and post-install handlers receive the same `InstallPayload` type: `{ previousVersion?: string; newVersion: string }`. Import it once and reuse it for both hooks.
|
||||
* **When the hook runs**: positioned just before the workspace metadata migration (`synchronizeFromManifest`). Before executing, the server runs a purely additive "pared-down sync" that registers the **new** version's pre-install function in the workspace metadata — nothing else is touched — and then executes it. Because this sync is additive-only, the previous version's objects, fields, and data are still intact when your handler runs: you can safely read and back up pre-migration state.
|
||||
* **Execution model**: pre-install is executed **synchronously** and **blocks the install**. If the handler throws, the install is aborted before any schema changes are applied — the workspace stays on the previous version in a consistent state. This is intentional: pre-install is your last chance to refuse a risky upgrade.
|
||||
* As with post-install, only one pre-install function is allowed per application. It is attached to the application manifest under `preInstallLogicFunction` automatically during the build.
|
||||
* **Not executed in dev mode**: same as post-install — the install flow is skipped entirely for locally-registered apps, so pre-install never runs under `yarn twenty dev`. Use `yarn twenty exec --preInstall` to trigger it manually.
|
||||
Hauptpunkte:
|
||||
* Pre-Install-Funktionen verwenden `definePreInstallLogicFunction()` — dieselbe spezialisierte Konfiguration wie bei Post-Install, nur an einen anderen Lifecycle-Slot gebunden.
|
||||
* Sowohl Pre- als auch Post-Install-Handler erhalten denselben `InstallPayload`-Typ: `{ previousVersion?: string; newVersion: string }`. Importieren Sie ihn einmal und verwenden Sie ihn für beide Hooks wieder.
|
||||
* **Wann der Hook ausgeführt wird**: positioniert direkt vor der Metadatenmigration des Workspaces (`synchronizeFromManifest`). Vor der Ausführung führt der Server einen rein additiven "pared-down sync" durch, der die Pre-Install-Funktion der **neuen** Version in den Workspace-Metadaten registriert — sonst wird nichts angefasst — und führt sie dann aus. Da dieser Sync nur additiv ist, sind die Objekte, Felder und Daten der vorherigen Version noch intakt, wenn Ihr Handler läuft: Sie können den Zustand vor der Migration gefahrlos lesen und sichern.
|
||||
* **Ausführungsmodell**: Pre-Install wird **synchron** ausgeführt und **blockiert die Installation**. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor Schemaänderungen angewendet werden — der Workspace verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen.
|
||||
* Wie bei Post-Install ist pro Anwendung nur eine Pre-Installationsfunktion zulässig. Sie wird während des Builds automatisch dem Anwendungsmanifest unter `preInstallLogicFunction` hinzugefügt.
|
||||
* **Nicht im Dev-Modus ausgeführt**: wie bei Post-Install — der Installationsablauf wird für lokal registrierte Apps vollständig übersprungen, daher läuft Pre-Install unter `yarn twenty dev` nie. Verwenden Sie `yarn twenty exec --preInstall`, um es manuell auszulösen.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Pre-install vs post-install: when to use which" description="Choosing the right install hook">
|
||||
<Accordion title="Pre-Install vs. Post-Install: wann was verwenden" description="Den richtigen Installations-Hook wählen">
|
||||
|
||||
Both hooks are part of the same install flow and receive the same `InstallPayload`. The difference is **when** they run relative to the workspace metadata migration, and that changes what data they can safely touch.
|
||||
Beide Hooks sind Teil desselben Installationsablaufs und erhalten dasselbe `InstallPayload`. Der Unterschied besteht darin, **wann** sie relativ zur Metadatenmigration des Workspaces ausgeführt werden, und das ändert, auf welche Daten sie gefahrlos zugreifen können.
|
||||
|
||||
Pre-install is always **synchronous** (it blocks the install and can abort it). Post-install is **asynchronous by default** — enqueued on a worker with automatic retries — but can opt into synchronous execution with `shouldRunSynchronously: true`. See the `definePostInstallLogicFunction` accordion above for when to use each mode.
|
||||
Pre-Install ist immer **synchron** (blockiert die Installation und kann sie abbrechen). Post-Install ist **standardmäßig asynchron** — in einen Worker eingereiht mit automatischen Wiederholungen — kann aber per `shouldRunSynchronously: true` in die synchrone Ausführung wechseln. Siehe das Akkordeon zu `definePostInstallLogicFunction` oben, wann welcher Modus zu verwenden ist.
|
||||
|
||||
**Use `post-install` for anything that needs the new schema to exist.** This is the common case:
|
||||
**Verwenden Sie `post-install` für alles, wofür das neue Schema existieren muss.** Dies ist der Regelfall:
|
||||
|
||||
* Seeding default data (creating initial records, default views, demo content) against newly-added objects and fields.
|
||||
* Registering webhooks with third-party services now that the app has its credentials.
|
||||
* Calling your own API to finish setup that depends on the synchronized metadata.
|
||||
* Idempotent "ensure this exists" logic that should reconcile state on every upgrade — combine with `shouldRunOnVersionUpgrade: true`.
|
||||
* Standarddaten befüllen (Anlegen anfänglicher Datensätze, Standardansichten, Demo-Inhalte) für neu hinzugefügte Objekte und Felder.
|
||||
* Registrieren von Webhooks bei Drittanbieter-Diensten, jetzt, da die App ihre Anmeldedaten hat.
|
||||
* Aufrufen Ihrer eigenen API, um eine Einrichtung abzuschließen, die von den synchronisierten Metadaten abhängt.
|
||||
* Idempotente "Stelle sicher, dass dies existiert"-Logik, die bei jedem Upgrade den Zustand abgleichen soll — kombinieren Sie dies mit `shouldRunOnVersionUpgrade: true`.
|
||||
|
||||
Example — seed a default `PostCard` record after install:
|
||||
Beispiel — nach der Installation einen Standard-`PostCard`-Datensatz anlegen:
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -137,14 +137,14 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Use `pre-install` when a migration would otherwise destroy or corrupt existing data.** Because pre-install runs against the *previous* schema and its failure rolls back the upgrade, it is the right place for anything risky:
|
||||
**Verwenden Sie `pre-install`, wenn eine Migration ansonsten vorhandene Daten löschen oder beschädigen würde.** Da Pre-Install gegen das vorherige Schema läuft und ein Fehlschlag das Upgrade zurückrollt, ist es der richtige Ort für alles Riskante:
|
||||
|
||||
* **Backing up data that is about to be dropped or restructured** — e.g. you are removing a field in v2 and need to copy its values into another field or export them to storage before the migration runs.
|
||||
* **Archiving records that a new constraint would invalidate** — e.g. a field is becoming `NOT NULL` and you need to delete or fix rows with null values first.
|
||||
* **Validating compatibility and refusing the upgrade if the current data cannot be migrated cleanly** — throw from the handler and the install aborts with no changes applied. This is safer than discovering the incompatibility mid-migration.
|
||||
* **Renaming or rekeying data** ahead of a schema change that would lose the association.
|
||||
* **Sichern von Daten, die gleich gelöscht oder umstrukturiert werden** — z. B. Sie entfernen in v2 ein Feld und müssen dessen Werte vor der Migration in ein anderes Feld kopieren oder in einen Speicher exportieren.
|
||||
* **Archivieren von Datensätzen, die eine neue Einschränkung ungültig machen würde** — z. B. ein Feld wird `NOT NULL` und Sie müssen zuerst Zeilen mit Null-Werten löschen oder korrigieren.
|
||||
* **Kompatibilität validieren und das Upgrade ablehnen, wenn die aktuellen Daten nicht sauber migriert werden können** — werfen Sie im Handler einen Fehler, und die Installation wird ohne Änderungen abgebrochen. Das ist sicherer, als die Inkompatibilität mitten in der Migration zu entdecken.
|
||||
* **Daten umbenennen oder Schlüssel neu zuweisen** vor einer Schemaänderung, bei der sonst die Zuordnung verloren ginge.
|
||||
|
||||
Example — archive records before a destructive migration:
|
||||
Beispiel — Datensätze vor einer destruktiven Migration archivieren:
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -186,20 +186,20 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Rule of thumb:**
|
||||
**Faustregel:**
|
||||
|
||||
| You want to... | Use |
|
||||
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| Seed default data, configure the workspace, register external resources | `post-install` |
|
||||
| Run long-running seeding or third-party calls that shouldn't block the install response | `post-install` (default — `shouldRunSynchronously: false`, with worker retries) |
|
||||
| Run fast setup that the caller will rely on immediately after the install call returns | `post-install` with `shouldRunSynchronously: true` |
|
||||
| Read or back up data that the upcoming migration would lose | `pre-install` |
|
||||
| Reject an upgrade that would corrupt existing data | `pre-install` (throw from the handler) |
|
||||
| Run reconciliation on every upgrade | `post-install` with `shouldRunOnVersionUpgrade: true` |
|
||||
| Do one-off setup on the first install only | `post-install` with `shouldRunOnVersionUpgrade: false` (default) |
|
||||
| Sie möchten ... | Verwenden |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| Standarddaten befüllen, den Workspace konfigurieren, externe Ressourcen registrieren | `post-install` |
|
||||
| Lang laufendes Seeding oder Drittanbieteraufrufe ausführen, die die Installationsantwort nicht blockieren sollten | `post-install` (Standard — `shouldRunSynchronously: false`, mit Worker-Wiederholungen) |
|
||||
| Schnelle Einrichtung ausführen, auf die sich der Aufrufer unmittelbar nach der Rückkehr des Installationsaufrufs verlassen wird | `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) |
|
||||
| Bei jedem Upgrade einen Abgleich ausführen | `post-install` mit `shouldRunOnVersionUpgrade: true` |
|
||||
| Einmalige Einrichtung nur bei der ersten Installation durchführen | `post-install` mit `shouldRunOnVersionUpgrade: false` (Standard) |
|
||||
|
||||
<Note>
|
||||
If in doubt, default to **post-install**. Only reach for pre-install when the migration itself is destructive and you need to intercept the previous state before it is gone.
|
||||
Im Zweifel auf **Post-Install** setzen. Greifen Sie nur zu Pre-Install, wenn die Migration selbst destruktiv ist und Sie den vorherigen Zustand abfangen müssen, bevor er verloren geht.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Configure the app itself — its identity, default permissions, and what runs at install time.
|
||||
title: Übersicht
|
||||
description: Konfigurieren Sie die App selbst – ihre Identität, Standardberechtigungen und das, was zur Installationszeit ausgeführt wird.
|
||||
icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
A Twenty app's **config layer** is what describes the app *to the platform* — its identity, the permissions it holds, and the code that runs during install or upgrade. These declarations don't add new data shapes or runtime behavior; they tell Twenty *who the app is* and *how to set it up*.
|
||||
Die **Konfigurationsebene** einer Twenty-App beschreibt die App *für die Plattform* – ihre Identität, die Berechtigungen, die sie hält, und den Code, der während der Installation oder Aktualisierung ausgeführt wird. Diese Deklarationen fügen keine neuen Datentypen oder Laufzeitverhalten hinzu; sie teilen Twenty mit, *wer die App ist* und *wie sie eingerichtet werden soll*.
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
@@ -26,26 +26,26 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## In diesem Abschnitt
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Application Config" icon="rocket" href="/l/de/developers/extend/apps/config/application">
|
||||
`defineApplication` — identity, default role, variables, marketplace metadata.
|
||||
<Card title="Anwendungskonfiguration" icon="rocket" href="/l/de/developers/extend/apps/config/application">
|
||||
`defineApplication` – Identität, Standardrolle, Variablen, Marketplace-Metadaten.
|
||||
</Card>
|
||||
<Card title="Roles & Permissions" icon="shield-halved" href="/l/de/developers/extend/apps/config/roles">
|
||||
`defineRole` — declare what your app's logic functions can read and write.
|
||||
<Card title="Rollen & Berechtigungen" icon="shield-halved" href="/l/de/developers/extend/apps/config/roles">
|
||||
`defineRole` – deklariert, was die Logikfunktionen Ihrer App lesen und schreiben können.
|
||||
</Card>
|
||||
<Card title="Install Hooks" icon="wrench" href="/l/de/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` and `definePostInstallLogicFunction` — back up data, seed defaults, validate upgrades.
|
||||
<Card title="Installations-Hooks" icon="Schraubenschlüssel" href="/l/de/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` und `definePostInstallLogicFunction` – Daten sichern, Standardwerte befüllen, Aktualisierungen validieren.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## How the pieces relate
|
||||
## Wie die Bausteine zusammenhängen
|
||||
|
||||
* **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).
|
||||
* **Application** ist der Einstiegspunkt. Jede App hat genau einen `defineApplication()`-Aufruf, und dieser verweist auf eine **Role** als Standard.
|
||||
* Die **Role** steuert, was die Logikfunktionen und Front-Komponenten der App lesen und schreiben können. Folgen Sie dem Prinzip der geringsten Privilegien: Gewähren Sie nur die Berechtigungen, die Ihr Code tatsächlich benötigt.
|
||||
* **Install Hooks** laufen während der Installation oder Aktualisierung – Pre-Install vor der Metadatenmigration (so kann ein riskantes Upgrade abgelehnt werden), Post-Install nach der Migration (so können Standarddaten gegen das neue Schema befüllt werden).
|
||||
|
||||
<Note>
|
||||
Install hooks share the [logic function](/l/de/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).
|
||||
Installations-Hooks nutzen die Laufzeit der [Logikfunktion](/l/de/developers/extend/apps/logic/logic-functions) – gleiche Handler-Signatur, gleiche Umgebungsvariablen, gleicher typisierter API-Client –, werden aber mit ihren eigenen Define-Funktionen deklariert und leben außerhalb des regulären Trigger-Modells (HTTP, Cron, Datenbankereignisse).
|
||||
</Note>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
---
|
||||
title: Public Assets
|
||||
description: Ship static files — images, icons, fonts — alongside your app via the public/ folder.
|
||||
title: Öffentliche Assets
|
||||
description: Liefere statische Dateien – Bilder, Symbole, Schriftarten – zusammen mit deiner App über den Ordner public/.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
The `public/` folder at the root of your app holds static files — images, icons, fonts, or any other assets your app needs at runtime. These files are automatically included in builds, synced during dev mode, and uploaded to the server.
|
||||
Der Ordner `public/` im Stammverzeichnis Ihrer App enthält statische Dateien — Bilder, Icons, Schriftarten oder sonstige Assets, die Ihre App zur Laufzeit benötigt. Diese Dateien werden automatisch in Builds aufgenommen, während des Dev-Modus synchronisiert und auf den Server hochgeladen.
|
||||
|
||||
Files placed in `public/` are:
|
||||
Für Dateien im Verzeichnis `public/` gilt:
|
||||
|
||||
* **Publicly accessible** — once synced to the server, assets are served at a public URL. No authentication is needed to access them.
|
||||
* **Available in front components** — use asset URLs to display images, icons, or any media inside your React components.
|
||||
* **Available in logic functions** — reference asset URLs in emails, API responses, or any server-side logic.
|
||||
* **Used for marketplace metadata** — the `logoUrl` and `screenshots` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published.
|
||||
* **Auto-synced in dev mode** — when you add, update, or delete a file in `public/`, it is synced to the server automatically. No restart needed.
|
||||
* **Included in builds** — `yarn twenty build` bundles all public assets into the distribution output.
|
||||
* **Öffentlich zugänglich** — nach der Synchronisierung mit dem Server werden Assets unter einer öffentlichen URL bereitgestellt. Zum Zugriff ist keine Authentifizierung erforderlich.
|
||||
* **In Frontend-Komponenten verfügbar** — verwenden Sie Asset-URLs, um Bilder, Icons oder andere Medien in Ihren React-Komponenten anzuzeigen.
|
||||
* **In Logikfunktionen verfügbar** — referenzieren Sie Asset-URLs in E-Mails, API-Antworten oder in beliebiger serverseitiger Logik.
|
||||
* **Für Marketplace-Metadaten verwendet** — die Felder `logoUrl` und `screenshots` in `defineApplication()` referenzieren Dateien aus diesem Ordner (z. B. `public/logo.png`). Diese werden im Marketplace angezeigt, wenn Ihre App veröffentlicht wird.
|
||||
* **Im Dev-Modus automatisch synchronisiert** — wenn Sie in `public/` eine Datei hinzufügen, aktualisieren oder löschen, wird sie automatisch mit dem Server synchronisiert. Kein Neustart erforderlich.
|
||||
* **In Builds enthalten** — `yarn twenty build` bündelt alle öffentlichen Assets in der Distributionsausgabe.
|
||||
|
||||
## Accessing public assets with `getPublicAssetUrl`
|
||||
## Zugriff auf öffentliche Assets mit `getPublicAssetUrl`
|
||||
|
||||
Use the `getPublicAssetUrl` helper from `twenty-sdk` to get the full URL of a file in your `public/` directory. It works in both **logic functions** and **front components**.
|
||||
Verwenden Sie den Helper `getPublicAssetUrl` aus `twenty-sdk`, um die vollständige URL einer Datei in Ihrem `public/`-Verzeichnis zu erhalten. Dies funktioniert sowohl in Logikfunktionen als auch in Frontend-Komponenten.
|
||||
|
||||
**In a logic function:**
|
||||
**In einer Logikfunktion:**
|
||||
|
||||
```ts src/logic-functions/send-invoice.ts
|
||||
import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -44,7 +44,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**In a front component:**
|
||||
**In einer Frontend-Komponente:**
|
||||
|
||||
```tsx src/front-components/company-card.tsx
|
||||
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -56,4 +56,4 @@ export default defineFrontComponent(() => {
|
||||
});
|
||||
```
|
||||
|
||||
The `path` argument is relative to your app's `public/` folder. Both `getPublicAssetUrl('logo.png')` and `getPublicAssetUrl('public/logo.png')` resolve to the same URL — the `public/` prefix is stripped automatically if present.
|
||||
Das Argument `path` ist relativ zum `public/`-Ordner Ihrer App. Sowohl `getPublicAssetUrl('logo.png')` als auch `getPublicAssetUrl('public/logo.png')` ergeben dieselbe URL — das Präfix `public/` wird, falls vorhanden, automatisch entfernt.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Roles & Permissions
|
||||
description: Declare what objects and fields your app's logic functions and front components can read and write.
|
||||
title: Rollen & Berechtigungen
|
||||
description: Legen Sie fest, welche Objekte und Felder die Logikfunktionen und Front-Komponenten Ihrer App lesen und schreiben können.
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
A **role** is a permission set: which objects an app can read or write, which fields it can see, and which platform-level capabilities it can use. Every app's logic functions and front components inherit the permissions of the role declared as `defaultRoleUniversalIdentifier` in [`defineApplication`](/l/de/developers/extend/apps/config/application).
|
||||
Eine **Rolle** ist ein Berechtigungssatz: welche Objekte eine App lesen oder schreiben kann, welche Felder sie sehen kann und welche plattformbezogenen Funktionen sie nutzen kann. Alle Logikfunktionen und Front-Komponenten einer App erben die Berechtigungen der Rolle, die in `defineApplication` als `defaultRoleUniversalIdentifier` deklariert ist ([`defineApplication`](/l/de/developers/extend/apps/config/application)).
|
||||
|
||||
```ts src/roles/restricted-company-role.ts
|
||||
import {
|
||||
@@ -49,9 +49,9 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
## The default function role
|
||||
## Die Standard-Funktionsrolle
|
||||
|
||||
When you scaffold a new app, the CLI creates a default role file:
|
||||
Wenn Sie eine neue App erzeugen, erstellt die CLI eine Standard-Rolldatei:
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineRole, PermissionFlag } from 'twenty-sdk/define';
|
||||
@@ -77,14 +77,14 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
This role's `universalIdentifier` is referenced from `application-config.ts` as `defaultRoleUniversalIdentifier`:
|
||||
Der `universalIdentifier` dieser Rolle wird in `application-config.ts` als `defaultRoleUniversalIdentifier` referenziert:
|
||||
|
||||
* **`*.role.ts`** declares what the role can do.
|
||||
* **`application-config.ts`** points to that role so your functions inherit its permissions.
|
||||
* **`*.role.ts`** deklariert, was die Rolle darf.
|
||||
* **`application-config.ts`** verweist auf diese Rolle, sodass Ihre Funktionen deren Berechtigungen erben.
|
||||
|
||||
## Best practices
|
||||
## Beste Praktiken
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it — the default grants broad read access, which is rarely what you want in production.
|
||||
* Replace `objectPermissions` and `fieldPermissions` with the exact objects and fields your functions actually need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal.
|
||||
* See a working example: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
* Beginnen Sie mit der vorgegebenen Rolle und schränken Sie sie dann schrittweise ein – standardmäßig wird umfangreicher Lesezugriff gewährt, was selten das ist, was Sie in Produktionsumgebungen möchten.
|
||||
* Ersetzen Sie `objectPermissions` und `fieldPermissions` durch die genauen Objekte und Felder, die Ihre Funktionen tatsächlich benötigen.
|
||||
* `permissionFlags` steuern den Zugriff auf Funktionen auf Plattformebene. Halten Sie sie minimal.
|
||||
* Ein funktionierendes Beispiel finden Sie unter: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Extending Objects
|
||||
description: Add fields to standard Twenty objects (Person, Company, …) or to objects from other apps using defineField.
|
||||
title: Objekte erweitern
|
||||
description: Fügen Sie Standard-Twenty-Objekten Felder hinzu (Person, Company, …) oder zu Objekten aus anderen Apps mit `defineField`.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Use `defineField()` to add a field to an object you don't own — a standard Twenty object like Person or Company, or an object shipped by another installed app. Unlike inline fields declared inside [`defineObject`](/l/de/developers/extend/apps/data/objects), standalone fields require an `objectUniversalIdentifier` to specify which object they extend.
|
||||
Verwenden Sie `defineField()`, um einem Objekt, das Ihnen nicht gehört, ein Feld hinzuzufügen – ein Standard-Twenty-Objekt wie Person oder Company oder ein Objekt, das von einer anderen installierten App bereitgestellt wird. Im Gegensatz zu Inline-Feldern, die innerhalb von [`defineObject`](/l/de/developers/extend/apps/data/objects) deklariert werden, benötigen eigenständige Felder einen `objectUniversalIdentifier`, um anzugeben, welches Objekt sie erweitern.
|
||||
|
||||
```ts src/fields/company-loyalty-tier.field.ts
|
||||
import { defineField, FieldType } from 'twenty-sdk/define';
|
||||
@@ -24,9 +24,9 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Hauptpunkte
|
||||
|
||||
* `objectUniversalIdentifier` identifies the target object. For standard Twenty objects, import the constant from `twenty-sdk`:
|
||||
* Der `objectUniversalIdentifier` identifiziert das Zielobjekt. Für Standard-Twenty-Objekte importieren Sie die Konstante aus `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
@@ -37,12 +37,12 @@ export default defineField({
|
||||
// …
|
||||
```
|
||||
|
||||
* When defining fields **inline inside `defineObject()`**, you do **not** need `objectUniversalIdentifier` — it's inherited from the parent object.
|
||||
* Wenn Sie Felder **inline innerhalb von `defineObject()`** definieren, benötigen Sie `objectUniversalIdentifier` **nicht** – es wird vom übergeordneten Objekt geerbt.
|
||||
|
||||
* `defineField()` is the only way to add fields to objects you didn't create with `defineObject()`.
|
||||
* `defineField()` ist die einzige Möglichkeit, Felder zu Objekten hinzuzufügen, die Sie nicht mit `defineObject()` erstellt haben.
|
||||
|
||||
* File location is up to you. The convention is `src/fields/\<name>.field.ts`, but the SDK detects fields anywhere in `src/`.
|
||||
* Der Speicherort der Datei liegt bei Ihnen. Die Konvention ist `src/fields/\<name>.field.ts`, aber das SDK erkennt Felder überall in `src/`.
|
||||
|
||||
## Adding a relation to an existing object
|
||||
## Hinzufügen einer Relation zu einem bestehenden Objekt
|
||||
|
||||
To add a relation field (e.g. linking your custom object to a standard `Person`), use `defineField()` with `FieldType.RELATION`. The pattern is the same as for inline relations but with `objectUniversalIdentifier` set explicitly. See [Relations](/l/de/developers/extend/apps/data/relations) for the bidirectional pattern.
|
||||
Um ein Relationsfeld hinzuzufügen (z. B. zur Verknüpfung Ihres benutzerdefinierten Objekts mit einer Standard-`Person`), verwenden Sie `defineField()` mit `FieldType.RELATION`. Das Muster ist dasselbe wie bei Inline-Relationen, jedoch mit explizit gesetztem `objectUniversalIdentifier`. Siehe [Relations](/l/de/developers/extend/apps/data/relations) für das bidirektionale Muster.
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Deklariere neue Record-Typen – benutzerdefinierte Tabellen mit e
|
||||
icon: tabelle
|
||||
---
|
||||
|
||||
Custom **objects** are new record types your app adds to a workspace — Post Card, Invoice, Subscription, anything specific to your domain. Each object declares its schema (fields, relations, default values) and a stable universal identifier that survives across syncs and deploys.
|
||||
Benutzerdefinierte **Objekte** sind neue Datensatztypen, die Ihre App zu einem Arbeitsbereich hinzufügt – Postkarte, Rechnung, Abonnement, alles, was spezifisch für Ihre Domäne ist. Jedes Objekt deklariert sein Schema (Felder, Relationen, Standardwerte) und einen stabilen universellen Bezeichner, der über Synchronisierungen und Deployments hinweg bestehen bleibt.
|
||||
|
||||
```ts src/objects/post-card.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
@@ -79,15 +79,15 @@ export default defineObject({
|
||||
* Der `universalIdentifier` muss eindeutig und über Deployments hinweg stabil sein.
|
||||
* Jedes Feld benötigt `name`, `type`, `label` und einen eigenen stabilen `universalIdentifier`.
|
||||
* Das Array `fields` ist optional — Sie können Objekte ohne benutzerdefinierte Felder definieren.
|
||||
* Inline fields defined here do **not** need an `objectUniversalIdentifier` — it's inherited from the parent object. Use [`defineField()`](/l/de/developers/extend/apps/data/extending-objects) to add fields to objects you don't own.
|
||||
* You can scaffold new objects with `yarn twenty add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/l/de/developers/extend/apps/getting-started/scaffolding).
|
||||
* Inline definierte Felder benötigen **kein** `objectUniversalIdentifier` – er wird vom übergeordneten Objekt geerbt. Verwenden Sie [`defineField()`](/l/de/developers/extend/apps/data/extending-objects), um Objekten Felder hinzuzufügen, die Ihnen nicht gehören.
|
||||
* Sie können mit `yarn twenty add object` neue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen. Siehe [Architektur → Gerüste für Entitäten](/l/de/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
<Note>
|
||||
**Base fields are added automatically.** When you define a custom object, Twenty creates standard fields like `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, and `deletedAt` for you. You don't need to declare them in your `fields` array — only your custom fields. You can override a default field by declaring one with the same name, but this is rarely a good idea.
|
||||
**Basisfelder werden automatisch hinzugefügt.** Wenn Sie ein benutzerdefiniertes Objekt definieren, erstellt Twenty Standardfelder wie `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` und `deletedAt` für Sie. Sie müssen diese nicht in Ihrem `fields`-Array deklarieren – nur Ihre benutzerdefinierten Felder. Sie können ein Standardfeld überschreiben, indem Sie eines mit demselben Namen deklarieren, aber das ist nur selten eine gute Idee.
|
||||
</Note>
|
||||
|
||||
## Was kommt als Nächstes
|
||||
|
||||
* **Connect this object to others** — see [Relations](/l/de/developers/extend/apps/data/relations) for the bidirectional relation pattern.
|
||||
* **Add fields to objects from other apps** — see [Extending Objects](/l/de/developers/extend/apps/data/extending-objects) for `defineField()`.
|
||||
* **Display this object in the UI** — see [Views](/l/de/developers/extend/apps/layout/views) and [Navigation Menu Items](/l/de/developers/extend/apps/layout/navigation-menu-items) to put it in the sidebar.
|
||||
* **Verbinden Sie dieses Objekt mit anderen** – siehe [Relationen](/l/de/developers/extend/apps/data/relations) für das bidirektionale Relationsmuster.
|
||||
* **Fügen Sie Objekten aus anderen Apps Felder hinzu** – siehe [Objekte erweitern](/l/de/developers/extend/apps/data/extending-objects) für `defineField()`.
|
||||
* **Zeigen Sie dieses Objekt in der UI an** – siehe [Ansichten](/l/de/developers/extend/apps/layout/views) und [Navigationsmenüeinträge](/l/de/developers/extend/apps/layout/navigation-menu-items), um es in der Seitenleiste zu platzieren.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Shape the data your app adds to a workspace — objects, fields, and relations.
|
||||
title: Übersicht
|
||||
description: Gestalten Sie die Daten, die Ihre App zu einem Workspace hinzufügt – Objekte, Felder und Beziehungen.
|
||||
icon: database
|
||||
---
|
||||
|
||||
A Twenty app's **data layer** is the data your app *adds* to a workspace — the new record types it declares, the columns it adds to existing objects, and how those records connect to each other.
|
||||
Die **Datenebene** einer Twenty-App umfasst die Daten, die Ihre App zu einem Workspace *hinzufügt* – die neuen Datensatztypen, die sie deklariert, die Spalten, die sie zu bestehenden Objekten hinzufügt, und wie diese Datensätze miteinander verknüpft sind.
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────┐
|
||||
@@ -23,30 +23,30 @@ A Twenty app's **data layer** is the data your app *adds* to a workspace — the
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## In diesem Abschnitt
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Objects" icon="table" href="/l/de/developers/extend/apps/data/objects">
|
||||
`defineObject` — declare new record types with their own fields.
|
||||
<Card title="Objekte" icon="table" href="/l/de/developers/extend/apps/data/objects">
|
||||
`defineObject` – deklarieren Sie neue Datensatztypen mit eigenen Feldern.
|
||||
</Card>
|
||||
<Card title="Extending Objects" icon="wand-magic-sparkles" href="/l/de/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — add fields to standard or other apps' objects.
|
||||
<Card title="Objekte erweitern" icon="wand-magic-sparkles" href="/l/de/developers/extend/apps/data/extending-objects">
|
||||
`defineField` – fügen Sie Standardobjekten oder Objekten anderer Apps Felder hinzu.
|
||||
</Card>
|
||||
<Card title="Relations" icon="diagram-project" href="/l/de/developers/extend/apps/data/relations">
|
||||
Bidirectional `MANY_TO_ONE` / `ONE_TO_MANY` connections between objects.
|
||||
<Card title="Beziehungen" icon="diagram-project" href="/l/de/developers/extend/apps/data/relations">
|
||||
Bidirektionale `MANY_TO_ONE`- / `ONE_TO_MANY`-Verbindungen zwischen Objekten.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Entities at a glance
|
||||
## Entitäten im Überblick
|
||||
|
||||
| Entity | Purpose | Defined with |
|
||||
| ------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Object** | A new custom record type (e.g. PostCard, Invoice) with its own fields | `defineObject()` |
|
||||
| **Field** | A column on an object. Standalone fields can extend objects you didn't create (e.g. add `loyaltyTier` to Company) | `defineField()` |
|
||||
| **Relation** | A bidirectional link between two objects — both sides declared as fields | `defineField()` with `FieldType.RELATION` |
|
||||
| Entität | Zweck | Definiert mit |
|
||||
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
|
||||
| **Objekt** | Ein neuer benutzerdefinierter Datensatztyp (z. B. PostCard, Invoice) mit eigenen Feldern | `defineObject()` |
|
||||
| **Feld** | Eine Spalte in einem Objekt. Eigenständige Felder können Objekte erweitern, die Sie nicht erstellt haben (z. B. `loyaltyTier` zu Company hinzufügen) | `defineField()` |
|
||||
| **Beziehung** | Eine bidirektionale Verknüpfung zwischen zwei Objekten – beide Seiten werden als Felder deklariert | `defineField()` mit `FieldType.RELATION` |
|
||||
|
||||
The SDK detects these via AST analysis at build time, so file organization is up to you — the convention is `src/objects/` and `src/fields/`. Stable `universalIdentifier` UUIDs tie everything together across deploys.
|
||||
Das SDK erkennt diese zur Build-Zeit über eine AST-Analyse, sodass die Dateiorganisation Ihnen überlassen ist – die Konvention ist `src/objects/` und `src/fields/`. Stabile `universalIdentifier`-UUIDs verknüpfen alles über Deploys hinweg.
|
||||
|
||||
<Note>
|
||||
Looking for **Application Config** or **Roles & Permissions**? Those describe the app itself rather than the data it adds — they live under [Config](/l/de/developers/extend/apps/config/overview). Looking for **Connections** (Linear, GitHub, Slack OAuth)? Those exist to be called *from* logic functions and live under [Logic](/l/de/developers/extend/apps/logic/connections).
|
||||
Suchen Sie nach **Application Config** oder **Roles & Permissions**? Diese beschreiben die App selbst und nicht die Daten, die sie hinzufügt – sie befinden sich unter [Config](/l/de/developers/extend/apps/config/overview). Suchen Sie nach **Connections** (Linear, GitHub, Slack OAuth)? Diese existieren, um *von* Logikfunktionen aufgerufen zu werden, und befinden sich unter [Logic](/l/de/developers/extend/apps/logic/connections).
|
||||
</Note>
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
title: Beziehungen
|
||||
description: Connect objects together with bidirectional MANY_TO_ONE / ONE_TO_MANY relations.
|
||||
description: Objekte mit bidirektionalen MANY_TO_ONE- / ONE_TO_MANY-Relationen verbinden.
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
Relations connect two objects together. In Twenty, relations are always **bidirectional** — every relation has two sides, and each side is declared as a field that references the other.
|
||||
Relationen verbinden zwei Objekte miteinander. In Twenty sind Relationen stets **bidirektional** — jede Relation hat zwei Seiten, und jede Seite wird als Feld deklariert, das auf die andere verweist.
|
||||
|
||||
| Beziehungstyp | Beschreibung | Fremdschlüssel vorhanden? |
|
||||
| ------------- | ----------------------------------------------------------------------- | ------------------------- |
|
||||
| `MANY_TO_ONE` | Viele Datensätze dieses Objekts verweisen auf einen Datensatz des Ziels | Ja (`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | Ein Datensatz dieses Objekts hat viele Datensätze des Ziels | No (the inverse side) |
|
||||
| `ONE_TO_MANY` | Ein Datensatz dieses Objekts hat viele Datensätze des Ziels | Nein (die inverse Seite) |
|
||||
|
||||
## How relations work
|
||||
## Wie Relationen funktionieren
|
||||
|
||||
Every relation requires **two fields** that reference each other:
|
||||
Jede Relation erfordert **zwei Felder**, die sich gegenseitig referenzieren:
|
||||
|
||||
1. The **MANY_TO_ONE** side — lives on the object that holds the foreign key.
|
||||
2. The **ONE_TO_MANY** side — lives on the object that owns the collection.
|
||||
1. Die **MANY_TO_ONE**-Seite — befindet sich auf dem Objekt, das den Fremdschlüssel hält.
|
||||
2. Die **ONE_TO_MANY**-Seite — befindet sich auf dem Objekt, dem die Sammlung gehört.
|
||||
|
||||
Both fields use `FieldType.RELATION` and cross-reference each other via `relationTargetFieldMetadataUniversalIdentifier`.
|
||||
Beide Felder verwenden `FieldType.RELATION` und verweisen über `relationTargetFieldMetadataUniversalIdentifier` gegenseitig aufeinander.
|
||||
|
||||
## Example: Post Card has many Recipients
|
||||
## Beispiel: Postkarte hat viele Empfänger
|
||||
|
||||
A `PostCard` can be sent to many `PostCardRecipient` records. Each recipient belongs to exactly one post card.
|
||||
Eine `PostCard` kann an viele `PostCardRecipient`-Datensätze gesendet werden. Jeder Empfänger gehört genau zu einer Postkarte.
|
||||
|
||||
**Step 1: Define the ONE_TO_MANY side on PostCard** (the "one" side):
|
||||
**Schritt 1: Definieren Sie die ONE_TO_MANY-Seite auf PostCard** (die "eine" Seite):
|
||||
|
||||
```ts src/fields/post-card-recipients-on-post-card.field.ts
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
|
||||
@@ -51,7 +51,7 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Define the MANY_TO_ONE side on PostCardRecipient** (the "many" side — holds the foreign key):
|
||||
**Schritt 2: Definieren Sie die MANY_TO_ONE-Seite auf PostCardRecipient** (die "viele" Seite — hält den Fremdschlüssel):
|
||||
|
||||
```ts src/fields/post-card-on-post-card-recipient.field.ts
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
|
||||
@@ -81,12 +81,12 @@ export default defineField({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Circular imports:** both relation fields reference each other's `universalIdentifier`. To avoid circular import issues, export your field IDs as named constants from each file and import them in the other. The build system resolves these at compile time.
|
||||
**Zyklische Importe:** Beide Relationsfelder referenzieren gegenseitig den `universalIdentifier` des jeweils anderen. Um Probleme mit zyklischen Importen zu vermeiden, exportieren Sie Ihre Feld-IDs als benannte Konstanten aus jeder Datei und importieren Sie sie in der jeweils anderen. Das Build-System löst dies zur Kompilierzeit auf.
|
||||
</Note>
|
||||
|
||||
## Relating to standard objects
|
||||
## Relationen zu Standardobjekten
|
||||
|
||||
To create a relation with a built-in Twenty object (Person, Company, etc.), use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
Um eine Relation mit einem integrierten Twenty-Objekt (Person, Company usw.) zu erstellen, verwenden Sie `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
|
||||
```ts src/fields/person-on-self-hosting-user.field.ts
|
||||
import {
|
||||
@@ -120,20 +120,20 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Relation field properties
|
||||
## Eigenschaften von Relationsfeldern
|
||||
|
||||
| Property | Required | Description |
|
||||
| ------------------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `type` | Yes | Must be `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the target object |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the matching field on the target object |
|
||||
| `universalSettings.relationType` | Yes | `RelationType.MANY_TO_ONE` or `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | MANY_TO_ONE only | What happens when the referenced record is deleted: `CASCADE`, `SET_NULL`, `RESTRICT`, or `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | MANY_TO_ONE only | Database column name for the foreign key (e.g., `postCardId`) |
|
||||
| Eigenschaft | Erforderlich | Beschreibung |
|
||||
| ------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `type` | Ja | Muss `FieldType.RELATION` sein |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Ja | Der `universalIdentifier` des Zielobjekts |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Ja | Der `universalIdentifier` des entsprechenden Felds auf dem Zielobjekt |
|
||||
| `universalSettings.relationType` | Ja | `RelationType.MANY_TO_ONE` oder `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | Nur für MANY_TO_ONE | Was passiert, wenn der referenzierte Datensatz gelöscht wird: `CASCADE`, `SET_NULL`, `RESTRICT` oder `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | Nur für MANY_TO_ONE | Datenbankspaltenname für den Fremdschlüssel (z. B. `postCardId`) |
|
||||
|
||||
## Inline relation fields
|
||||
## Inline-Relationsfelder
|
||||
|
||||
You can also declare a relation directly inside [`defineObject`](/l/de/developers/extend/apps/data/objects). When inline, omit `objectUniversalIdentifier` — it's inherited from the parent object:
|
||||
Sie können eine Relation auch direkt innerhalb von [`defineObject`](/l/de/developers/extend/apps/data/objects) deklarieren. Wenn inline, lassen Sie `objectUniversalIdentifier` weg — er wird vom übergeordneten Objekt geerbt:
|
||||
|
||||
```ts
|
||||
export default defineObject({
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: How Twenty apps work — entity model, sandboxing, and the install lifecycle.
|
||||
title: Konzepte
|
||||
description: Funktionsweise von Twenty-Apps – Entity-Modell, Sandboxing und der Installations-Lebenszyklus.
|
||||
icon: sitemap
|
||||
---
|
||||
|
||||
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
|
||||
Twenty-Apps sind TypeScript-Pakete, die Ihren Arbeitsbereich mit benutzerdefinierten Objekten, Logik, UI-Komponenten und KI-Funktionen erweitern. Sie laufen auf der Twenty-Plattform mit vollständigem Sandboxing und Berechtigungsverwaltung.
|
||||
|
||||
## How apps work
|
||||
## Wie Apps funktionieren
|
||||
|
||||
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace. These functions validate your configuration at build time and provide IDE autocompletion and type safety.
|
||||
Eine App ist eine Sammlung von **Entitäten**, die mithilfe von `defineEntity()`-Funktionen aus dem Paket `twenty-sdk` deklariert werden. Das SDK erkennt diese Deklarationen zur Build-Zeit per AST-Analyse und erzeugt ein **Manifest** — eine vollständige Beschreibung dessen, was Ihre App zu einem Arbeitsbereich hinzufügt. Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit.
|
||||
|
||||
```
|
||||
your-app/
|
||||
@@ -29,35 +29,35 @@ your-app/
|
||||
```
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
|
||||
**Die Dateiorganisation liegt bei Ihnen.** Die Entitätserkennung ist AST-basiert — das SDK findet Aufrufe von `export default defineEntity(...)`, unabhängig davon, wo sich die Datei befindet. Die obige Ordnerstruktur ist eine Konvention, keine Anforderung.
|
||||
</Note>
|
||||
|
||||
## Entity types
|
||||
## Entitätstypen
|
||||
|
||||
| Entity | Purpose | Docs |
|
||||
| ------------------------ | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| **Application** | App identity, default role, variables | [Application Config](/l/de/developers/extend/apps/config/application) |
|
||||
| **Role** | Permission sets on objects and fields | [Roles & Permissions](/l/de/developers/extend/apps/config/roles) |
|
||||
| **Object** | Custom record types with fields | [Objects](/l/de/developers/extend/apps/data/objects) |
|
||||
| **Field** | Add fields to objects from other apps | [Extending Objects](/l/de/developers/extend/apps/data/extending-objects) |
|
||||
| **Relation** | Bidirectional links between objects | [Relations](/l/de/developers/extend/apps/data/relations) |
|
||||
| **Logic Function** | Server-side TypeScript with triggers | [Logic Functions](/l/de/developers/extend/apps/logic/logic-functions) |
|
||||
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/de/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/de/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Connection Provider** | OAuth credentials for third-party APIs | [Connections](/l/de/developers/extend/apps/logic/connections) |
|
||||
| **View** | Pre-configured record list views | [Views](/l/de/developers/extend/apps/layout/views) |
|
||||
| **Navigation Menu Item** | Custom sidebar entries | [Navigation Menu Items](/l/de/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Page Layout** | Tabs and widgets on a record's detail page | [Page Layouts](/l/de/developers/extend/apps/layout/page-layouts) |
|
||||
| **Front Component** | Sandboxed React UI inside Twenty | [Front Components](/l/de/developers/extend/apps/layout/front-components) |
|
||||
| **Command Menu Item** | Quick actions and Cmd+K entries | [Command Menu Items](/l/de/developers/extend/apps/layout/command-menu-items) |
|
||||
| Entität | Zweck | Dokumentation |
|
||||
| -------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| **Anwendung** | App-Identität, Standardrolle, Variablen | [Application Config](/l/de/developers/extend/apps/config/application) |
|
||||
| **Rolle** | Berechtigungssätze für Objekte und Felder | [Roles & Permissions](/l/de/developers/extend/apps/config/roles) |
|
||||
| **Objekt** | Benutzerdefinierte Datensatztypen mit Feldern | [Objekte](/l/de/developers/extend/apps/data/objects) |
|
||||
| **Feld** | Felder zu Objekten aus anderen Apps hinzufügen | [Extending Objects](/l/de/developers/extend/apps/data/extending-objects) |
|
||||
| **Beziehung** | Bidirektionale Verknüpfungen zwischen Objekten | [Beziehungen](/l/de/developers/extend/apps/data/relations) |
|
||||
| **Logikfunktion** | Serverseitiges TypeScript mit Triggern | [Logikfunktionen](/l/de/developers/extend/apps/logic/logic-functions) |
|
||||
| **Skill** | Wiederverwendbare Anweisungen für KI-Agenten | [Skills & Agenten](/l/de/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | KI-Assistenten mit benutzerdefinierten Prompts | [Skills & Agenten](/l/de/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Verbindungsanbieter** | OAuth-Zugangsdaten für Drittanbieter-APIs | [Connections](/l/de/developers/extend/apps/logic/connections) |
|
||||
| **Ansicht** | Vorkonfigurierte Listenansichten für Datensätze | [Ansichten](/l/de/developers/extend/apps/layout/views) |
|
||||
| **Navigationsmenüeintrag** | Benutzerdefinierte Seitenleisten-Einträge | [Navigationsmenüeinträge](/l/de/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Seitenlayout** | Tabs und Widgets auf der Detailseite eines Datensatzes | [Seiten-Layouts](/l/de/developers/extend/apps/layout/page-layouts) |
|
||||
| **Frontend-Komponente** | Gedisplayte React-UI in einer Sandbox innerhalb von Twenty | [Frontend-Komponenten](/l/de/developers/extend/apps/layout/front-components) |
|
||||
| **Befehlsmenü-Eintrag** | Schnellaktionen und Cmd+K-Einträge | [Befehlsmenü-Einträge](/l/de/developers/extend/apps/layout/command-menu-items) |
|
||||
|
||||
## Sandboxing
|
||||
|
||||
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
|
||||
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
|
||||
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
|
||||
* **Logikfunktionen** laufen in isolierten Node.js-Prozessen auf dem Server. Sie greifen nur über den typisierten API-Client auf Daten zu, begrenzt durch die Rollenberechtigungen der App.
|
||||
* **Frontend-Komponenten** laufen in Web Workers mit Remote DOM — von der Hauptseite isoliert, rendern aber native DOM-Elemente (keine iframes). Sie kommunizieren über eine Message-Passing-Host-API mit Twenty.
|
||||
* **Berechtigungen** werden auf API-Ebene durchgesetzt. Das Laufzeit-Token (`TWENTY_APP_ACCESS_TOKEN`) wird aus der in `defineApplication()` definierten Rolle abgeleitet.
|
||||
|
||||
## App lifecycle
|
||||
## App-Lebenszyklus
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
@@ -76,26 +76,26 @@ 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 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](/l/de/developers/extend/apps/config/install-hooks) for details.
|
||||
* **`yarn twenty dev`** — überwacht Ihre Quelldateien und synchronisiert Änderungen in Echtzeit mit einem verbundenen Twenty-Server. Der typisierte API-Client wird automatisch neu erzeugt, wenn sich das Schema ändert.
|
||||
* **`yarn twenty build`** — kompiliert TypeScript, bündelt Logikfunktionen und Frontend-Komponenten mit esbuild und erzeugt ein Manifest.
|
||||
* **Pre/Post-Install-Hooks** — optionale Funktionen, die während der Installation ausgeführt werden. Details finden Sie unter [Install Hooks](/l/de/developers/extend/apps/config/install-hooks).
|
||||
|
||||
## Next steps
|
||||
## Nächste Schritte
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/de/developers/extend/apps/config/overview">
|
||||
Application identity, default role, and install hooks.
|
||||
<Card title="Konfiguration" icon="screwdriver-wrench" href="/l/de/developers/extend/apps/config/overview">
|
||||
App-Identität, Standardrolle und Install-Hooks.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/de/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
<Card title="Daten" icon="database" href="/l/de/developers/extend/apps/data/overview">
|
||||
Objekte, Felder und bidirektionale Relationen.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/de/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Logik" icon="bolt" href="/l/de/developers/extend/apps/logic/overview">
|
||||
Logikfunktionen, Skills, Agents und OAuth-Verbindungen.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/de/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Ansichten, Navigation, Seiten-Layouts, Front-Komponenten.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/de/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Operationen" icon="rocket" href="/l/de/developers/extend/apps/operations/overview">
|
||||
CLI, Tests, Remotes, CI und das Veröffentlichen Ihrer App.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
---
|
||||
title: Local Server
|
||||
description: Manage the local Twenty Docker server — start, stop, upgrade, parallel test instance, and manual SDK setup.
|
||||
title: Lokaler Server
|
||||
description: Den lokalen Twenty Docker-Server verwalten – starten, stoppen, aktualisieren, parallele Testinstanz und manuelle SDK-Einrichtung.
|
||||
icon: server
|
||||
---
|
||||
|
||||
## Managing the local server
|
||||
## Lokalen Server verwalten
|
||||
|
||||
Use `yarn twenty server` to control the local Twenty container:
|
||||
Verwenden Sie `yarn twenty server`, um den lokalen Twenty-Container zu steuern:
|
||||
|
||||
| Command | What it does |
|
||||
| -------------------------------------- | -------------------------------------------- |
|
||||
| `yarn twenty server start` | Start the server (pulls the image if needed) |
|
||||
| `yarn twenty server start --port 3030` | Start on a custom port |
|
||||
| `yarn twenty server stop` | Stop the server (preserves data) |
|
||||
| `yarn twenty server status` | Show URL, version, and login credentials |
|
||||
| `yarn twenty server logs` | Stream server logs |
|
||||
| `yarn twenty server reset` | Wipe data and start fresh |
|
||||
| `yarn twenty server upgrade` | Pull the latest `twenty-app-dev` image |
|
||||
| `yarn twenty server upgrade 2.2.0` | Upgrade to a specific version |
|
||||
| Befehl | Was es tut |
|
||||
| -------------------------------------- | --------------------------------------------------- |
|
||||
| `yarn twenty server start` | Server starten (lädt das Image bei Bedarf herunter) |
|
||||
| `yarn twenty server start --port 3030` | Auf einem benutzerdefinierten Port starten |
|
||||
| `yarn twenty server stop` | Server stoppen (Daten bleiben erhalten) |
|
||||
| `yarn twenty server status` | URL, Version und Anmeldedaten anzeigen |
|
||||
| `yarn twenty server logs` | Serverprotokolle streamen |
|
||||
| `yarn twenty server reset` | Alle Daten löschen und neu starten |
|
||||
| `yarn twenty server upgrade` | Das neueste `twenty-app-dev`-Image herunterladen |
|
||||
| `yarn twenty server upgrade 2.2.0` | Auf eine bestimmte Version aktualisieren |
|
||||
|
||||
Data persists across restarts in two Docker volumes (`twenty-app-dev-data` for PostgreSQL, `twenty-app-dev-storage` for files). Use `reset` to wipe everything.
|
||||
Daten bleiben über Neustarts hinweg in zwei Docker-Volumes bestehen (`twenty-app-dev-data` für PostgreSQL, `twenty-app-dev-storage` für Dateien). Verwenden Sie `reset`, um alles zu löschen.
|
||||
|
||||
## Upgrading the server image
|
||||
## Aktualisieren des Server-Images
|
||||
|
||||
`yarn twenty server upgrade` pulls the latest image, compares digests, and only recreates the container if anything actually changed. Volumes are preserved — only the container is replaced. If a new image was pulled and the container was running, the upgrade automatically starts a new container; run `yarn twenty server start` afterward to wait for it to become healthy.
|
||||
`yarn twenty server upgrade` lädt das neueste Image herunter, vergleicht die Digests und erstellt den Container nur neu, wenn sich tatsächlich etwas geändert hat. Die Volumes bleiben erhalten — nur der Container wird ersetzt. Wenn ein neues Image heruntergeladen wurde und der Container lief, startet das Upgrade automatisch einen neuen Container; führen Sie anschließend `yarn twenty server start` aus, um zu warten, bis er betriebsbereit ist.
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty server upgrade # Latest
|
||||
yarn twenty server upgrade 2.2.0 # Specific version
|
||||
```
|
||||
|
||||
Verify the running version with `yarn twenty server status` (it shows the `APP_VERSION` baked into the container).
|
||||
Überprüfen Sie die laufende Version mit `yarn twenty server status` (dies zeigt die im Container enthaltene `APP_VERSION` an).
|
||||
|
||||
## Running a parallel test instance
|
||||
## Eine parallele Testinstanz ausführen
|
||||
|
||||
Pass `--test` to any `server` command to manage a second, fully isolated instance — useful for integration tests or experiments without touching your main dev data:
|
||||
Übergeben Sie `--test` an jeden `server`-Befehl, um eine zweite, vollständig isolierte Instanz zu verwalten — nützlich für Integrationstests oder Experimente, ohne Ihre Hauptentwicklungsdaten anzutasten:
|
||||
|
||||
| Command | What it does |
|
||||
| ----------------------------------- | ----------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Start the test instance (defaults to port 2021) |
|
||||
| `yarn twenty server stop --test` | Stop it |
|
||||
| `yarn twenty server status --test` | Show its status |
|
||||
| `yarn twenty server logs --test` | Stream its logs |
|
||||
| `yarn twenty server reset --test` | Wipe its data |
|
||||
| `yarn twenty server upgrade --test` | Upgrade its image |
|
||||
| Befehl | Was es tut |
|
||||
| ----------------------------------- | ------------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Die Testinstanz starten (standardmäßig Port 2021) |
|
||||
| `yarn twenty server stop --test` | Anhalten |
|
||||
| `yarn twenty server status --test` | Status anzeigen |
|
||||
| `yarn twenty server logs --test` | Protokolle streamen |
|
||||
| `yarn twenty server reset --test` | Daten löschen |
|
||||
| `yarn twenty server upgrade --test` | Image aktualisieren |
|
||||
|
||||
The test instance has its own container (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`), and config — it runs alongside your main instance without conflicts. Combine `--test` with `--port` to override 2021.
|
||||
Die Testinstanz hat ihren eigenen Container (`twenty-app-dev-test`), eigene Volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) und eine eigene Konfiguration — sie läuft parallel zu Ihrer Hauptinstanz ohne Konflikte. Kombinieren Sie `--test` mit `--port`, um den Port 2021 zu überschreiben.
|
||||
|
||||
## Manual setup (without the scaffolder)
|
||||
## Manuelle Einrichtung (ohne Scaffolding-Tool)
|
||||
|
||||
Skip the scaffolder if you're adding the SDK to an existing project:
|
||||
Überspringen Sie das Scaffolding-Tool, wenn Sie das SDK zu einem bestehenden Projekt hinzufügen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
```
|
||||
|
||||
Add the script to `package.json`:
|
||||
Fügen Sie der `package.json` das Skript hinzu:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -65,8 +65,8 @@ Add the script to `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
You can now run `yarn twenty dev`, `yarn twenty server start`, and the rest.
|
||||
Sie können jetzt `yarn twenty dev`, `yarn twenty server start` und den Rest ausführen.
|
||||
|
||||
<Note>
|
||||
Don't install `twenty-sdk` globally — pin it per project so each app uses its own version.
|
||||
Installieren Sie `twenty-sdk` nicht global — fixieren Sie es pro Projekt, damit jede App ihre eigene Version verwendet.
|
||||
</Note>
|
||||
|
||||
+12
-12
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: What's inside a scaffolded Twenty app — files, folders, and what each one does.
|
||||
title: Projektstruktur
|
||||
description: Was in einer generierten Twenty-App enthalten ist – Dateien, Ordner und was jede einzelne davon macht.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
A new app generated by `npx create-twenty-app` looks like this:
|
||||
Eine neue App, die mit `npx create-twenty-app` generiert wurde, sieht so aus:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
@@ -25,16 +25,16 @@ my-twenty-app/
|
||||
README.md, LLMS.md
|
||||
```
|
||||
|
||||
## Key files
|
||||
## Wichtige Dateien
|
||||
|
||||
| File / Folder | Purpose |
|
||||
| ---------------------------------------- | -------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Required.** The main configuration file for your app. |
|
||||
| `src/default-role.ts` | Default role controlling what your logic functions can access. |
|
||||
| `src/constants/universal-identifiers.ts` | Auto-generated UUIDs and metadata (display name, description). |
|
||||
| `src/__tests__/` | Integration tests (setup + example test). |
|
||||
| `public/` | Static assets (images, fonts) served with your app. |
|
||||
| Datei / Ordner | Zweck |
|
||||
| ---------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Erforderlich.** Die Hauptkonfigurationsdatei für Ihre App. |
|
||||
| `src/default-role.ts` | Standardrolle, die steuert, worauf Ihre Logikfunktionen zugreifen können. |
|
||||
| `src/constants/universal-identifiers.ts` | Automatisch erzeugte UUIDs und Metadaten (Anzeigename, Beschreibung). |
|
||||
| `src/__tests__/` | Integrationstests (Setup + Beispieltest). |
|
||||
| `public/` | Statische Assets (Bilder, Schriftarten), die mit Ihrer App ausgeliefert werden. |
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** The folders above are conventions — the SDK detects entities via AST analysis on `export default defineEntity(...)` calls regardless of where the file lives.
|
||||
**Die Dateiorganisation liegt bei Ihnen.** Die oben genannten Ordner sind Konventionen – das SDK erkennt Entitäten über eine AST-Analyse von `export default defineEntity(...)`-Aufrufen, unabhängig davon, wo sich die Datei befindet.
|
||||
</Note>
|
||||
|
||||
@@ -1,184 +1,184 @@
|
||||
---
|
||||
title: Quick Start
|
||||
title: Schnellstart
|
||||
icon: rocket
|
||||
description: Create your first Twenty app in minutes.
|
||||
description: Erstellen Sie in wenigen Minuten Ihre erste Twenty-App.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
## Voraussetzungen
|
||||
|
||||
* **Node.js 24+** — [Download](https://nodejs.org/)
|
||||
* **Yarn 4** — bundled with Node via Corepack. Enable it: `corepack enable`
|
||||
* **Docker** — [Download](https://www.docker.com/products/docker-desktop/). Needed to run a local Twenty server. Skip if you already have Twenty running elsewhere.
|
||||
* **Node.js 24+** — [Hier herunterladen](https://nodejs.org/)
|
||||
* **Yarn 4** — Wird mit Node.js über Corepack mitgeliefert. Aktivieren Sie es: `corepack enable`
|
||||
* **Docker** — [Hier herunterladen](https://www.docker.com/products/docker-desktop/). Erforderlich, um einen lokalen Twenty-Server auszuführen. Überspringen Sie dies, wenn Twenty bereits anderswo läuft.
|
||||
|
||||
Building a Twenty app has three phases. The scaffolder collapses them into one happy-path command, but each phase is a separate concept — when something fails, knowing which phase you're in tells you what to fix.
|
||||
Das Erstellen einer Twenty-App umfasst drei Phasen. Das Scaffolding-Tool fasst sie zu einem einzigen Happy-Path-Befehl zusammen, aber jede Phase ist ein eigenes Konzept — wenn etwas fehlschlägt, hilft Ihnen das Wissen, in welcher Phase Sie sich befinden, zu erkennen, was zu beheben ist.
|
||||
|
||||
| Phase | What you do | Tool | Result |
|
||||
| ------------------- | ---------------------------------- | ----------------------------- | ----------------------------- |
|
||||
| **1. Scaffold** | Generate the app's source code | `npx create-twenty-app` | A TypeScript project on disk |
|
||||
| **2. Run a server** | Start a Twenty server to sync into | Docker + `yarn twenty server` | A running Twenty instance |
|
||||
| **3. Sync** | Live-sync your code to the server | `yarn twenty dev` | Your changes appear in the UI |
|
||||
| Phase | Was Sie tun | Tool | Ergebnis |
|
||||
| ----------------------- | ------------------------------------------------------- | ----------------------------- | ---------------------------------------------------- |
|
||||
| **1. Gerüst erstellen** | Den Quellcode der App erzeugen | `npx create-twenty-app` | Ein TypeScript-Projekt auf der Festplatte |
|
||||
| **2. Server starten** | Einen Twenty-Server starten, in den synchronisiert wird | Docker + `yarn twenty server` | Eine laufende Twenty-Instanz |
|
||||
| **3. Synchronisieren** | Ihren Code live mit dem Server synchronisieren | `yarn twenty dev` | Ihre Änderungen erscheinen in der Benutzeroberfläche |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Scaffold your project
|
||||
## Phase 1 — Projektgerüst erstellen
|
||||
|
||||
Create a new app from the template:
|
||||
Erstellen Sie eine neue App aus der Vorlage:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
You'll be prompted for a name and description — press **Enter** for the defaults. This generates a TypeScript project in `my-twenty-app/` with a starter `application-config.ts`, a default role, a CI workflow, and an integration test.
|
||||
Sie werden nach einem Namen und einer Beschreibung gefragt — drücken Sie **Enter** für die Standardwerte. Dadurch wird ein TypeScript-Projekt in `my-twenty-app/` erzeugt, mit einer Startdatei `application-config.ts`, einer Standardrolle, einem CI-Workflow und einem Integrationstest.
|
||||
|
||||
**After this phase:** you have an app's source code on your machine. It isn't running yet — that's Phase 2.
|
||||
**Nach dieser Phase:** Sie haben den Quellcode einer App auf Ihrem Rechner. Es läuft noch nicht — das ist Phase 2.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Run a local Twenty server
|
||||
## Phase 2 — Einen lokalen Twenty-Server starten
|
||||
|
||||
Your app needs a Twenty server to sync into. The server is a full Twenty instance — UI, GraphQL API, PostgreSQL — running locally in Docker. Your local code uploads its definitions to that server, which makes them appear in the UI.
|
||||
Ihre App benötigt einen Twenty-Server, in den sie synchronisieren kann. Der Server ist eine vollständige Twenty-Instanz — UI, GraphQL-API, PostgreSQL — die lokal in Docker läuft. Ihr lokaler Code lädt seine Definitionen auf diesen Server hoch, wodurch sie in der Benutzeroberfläche erscheinen.
|
||||
|
||||
The scaffolder offers to start one for you:
|
||||
Das Scaffolding-Tool bietet an, einen für Sie zu starten:
|
||||
|
||||
> **Would you like to set up a local Twenty instance?**
|
||||
> **Möchten Sie eine lokale Twenty-Instanz einrichten?**
|
||||
|
||||
* **Yes (recommended)** — pulls the `twentycrm/twenty-app-dev` Docker image and starts it on port `2020`. Make sure Docker is running first.
|
||||
* **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote add`.
|
||||
* **Ja (empfohlen)** — lädt das Docker-Image `twentycrm/twenty-app-dev` herunter und startet es auf Port `2020`. Stellen Sie sicher, dass Docker läuft.
|
||||
* **Nein** — wählen Sie dies, wenn Sie bereits einen Twenty-Server haben, mit dem Sie sich verbinden möchten. Sie können die Verbindung später mit `yarn twenty remote add` herstellen.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Soll die lokale Instanz gestartet werden?" />
|
||||
</div>
|
||||
|
||||
Once the server is up, a browser opens for sign-in. Use the pre-seeded demo account:
|
||||
Sobald der Server läuft, öffnet sich ein Browser zur Anmeldung. Verwenden Sie das vorab eingerichtete Demo-Konto:
|
||||
|
||||
* **Email:** `tim@apple.dev`
|
||||
* **Password:** `tim@apple.dev`
|
||||
* **E-Mail:** `tim@apple.dev`
|
||||
* **Passwort:** `tim@apple.dev`
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty login screen" />
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty-Anmeldebildschirm" />
|
||||
</div>
|
||||
|
||||
Click **Authorize** on the next screen — this gives the CLI access to your workspace.
|
||||
Klicken Sie auf dem nächsten Bildschirm auf **Authorize** — dadurch erhält die CLI Zugriff auf Ihren Arbeitsbereich.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty CLI authorization screen" />
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty-CLI-Autorisierungsbildschirm" />
|
||||
</div>
|
||||
|
||||
Your terminal will confirm everything is set up.
|
||||
Ihr Terminal bestätigt, dass alles eingerichtet ist.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="App scaffolded successfully" />
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="App-Gerüst erfolgreich erstellt" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a running Twenty server at [http://localhost:2020](http://localhost:2020) with your CLI authorized to sync to it.
|
||||
**Nach dieser Phase:** Sie haben einen laufenden Twenty-Server unter [http://localhost:2020](http://localhost:2020), und Ihre CLI ist autorisiert, mit ihm zu synchronisieren.
|
||||
|
||||
<Note>
|
||||
If Docker isn't installed or running, the scaffolder will tell you the right start command for your OS. Once Docker is up, you can resume with `yarn twenty server start` — no need to re-scaffold.
|
||||
Wenn Docker nicht installiert ist oder nicht läuft, zeigt das Scaffolding-Tool den richtigen Startbefehl für Ihr Betriebssystem an. Sobald Docker läuft, können Sie mit `yarn twenty server start` fortfahren — ein erneutes Scaffolding ist nicht nötig.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Sync your changes
|
||||
## Phase 3 — Ihre Änderungen synchronisieren
|
||||
|
||||
This is the inner loop you'll spend most of your time in.
|
||||
Das ist die innere Schleife, in der Sie die meiste Zeit verbringen werden.
|
||||
|
||||
```bash filename="Terminal"
|
||||
cd my-twenty-app
|
||||
yarn twenty dev
|
||||
```
|
||||
|
||||
This watches `src/`, rebuilds on every change, and syncs the result to the server. Edit a file, save, and within a second the server reflects the change. You'll see a live status panel in your terminal.
|
||||
Dies überwacht `src/`, baut bei jeder Änderung neu und synchronisiert das Ergebnis mit dem Server. Bearbeiten Sie eine Datei, speichern Sie, und innerhalb einer Sekunde spiegelt der Server die Änderung wider. Sie sehen eine Live-Statusanzeige in Ihrem Terminal.
|
||||
|
||||
For more detailed output (build logs, sync requests, error traces), add `--verbose`.
|
||||
Für ausführlichere Ausgaben (Build-Protokolle, Sync-Anfragen, Fehlerspuren) fügen Sie `--verbose` hinzu.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Dev mode terminal output" />
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Terminalausgabe im Dev-Modus" />
|
||||
</div>
|
||||
|
||||
Open [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). You should see your app under **Your Apps**.
|
||||
Öffnen Sie [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Unter **Your Apps** sollte Ihre App angezeigt werden.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Your Apps list showing My twenty app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Liste "Your Apps", die "My twenty app" anzeigt" />
|
||||
</div>
|
||||
|
||||
Click **My twenty app** to see its **application registration** — a server-level record describing your app (name, identifier, OAuth credentials, source). One registration can be installed across multiple workspaces on the same server.
|
||||
Klicken Sie auf **My twenty app**, um die **Anwendungsregistrierung** anzuzeigen — ein serverseitiger Datensatz, der Ihre App beschreibt (Name, Bezeichner, OAuth-Anmeldedaten, Quelle). Eine Registrierung kann in mehreren Arbeitsbereichen auf demselben Server installiert werden.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Application registration details" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Details der Anwendungsregistrierung" />
|
||||
</div>
|
||||
|
||||
Click **View installed app** to see the workspace install. The **About** tab shows version and management options.
|
||||
Klicken Sie auf **View installed app**, um die Installation im Arbeitsbereich anzuzeigen. Die Registerkarte **About** zeigt die Version und Verwaltungsoptionen.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Installed app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Installierte App" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a live development loop. Edit any file in `src/` and it appears in the UI.
|
||||
**Nach dieser Phase:** Sie haben eine Live-Entwicklungsschleife. Bearbeiten Sie eine beliebige Datei in `src/`, und sie erscheint in der Benutzeroberfläche.
|
||||
|
||||
### One-shot sync for CI and scripts
|
||||
### Einmalige Synchronisierung für CI und Skripte
|
||||
|
||||
Pass `--once` to run a single build + sync and exit — same pipeline, no watcher:
|
||||
Verwenden Sie `--once`, um einen einzelnen Build + Sync auszuführen und zu beenden — gleiche Pipeline, kein Watcher:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once
|
||||
```
|
||||
|
||||
| Command | Behavior | When to use |
|
||||
| ------------------------ | ------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `yarn twenty dev` | Watches and re-syncs on every change. Runs until you stop it. | Interactive local development. |
|
||||
| `yarn twenty dev --once` | Single build + sync, exits `0` on success, `1` on failure. | CI, pre-commit hooks, AI agents, scripted workflows. |
|
||||
| Befehl | Verhalten | Wann verwenden |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| `yarn twenty dev` | Überwacht und synchronisiert bei jeder Änderung erneut. Läuft, bis Sie es stoppen. | Interaktive lokale Entwicklung. |
|
||||
| `yarn twenty dev --once` | Einmaliger Build + Sync, beendet sich mit `0` bei Erfolg, mit `1` bei Fehler. | CI, Pre-Commit-Hooks, KI-Agenten, skriptgesteuerte Workflows. |
|
||||
|
||||
Both modes need a server in development mode and an authenticated remote.
|
||||
Beide Modi benötigen einen Server im Entwicklungsmodus und eine authentifizierte Remote-Verbindung.
|
||||
|
||||
<Warning>
|
||||
Dev mode is only available on Twenty instances running in development (`NODE_ENV=development`). Production instances reject dev sync requests — use `yarn twenty deploy` to deploy to production servers. See [Publishing](/l/de/developers/extend/apps/operations/publishing).
|
||||
Der Dev-Modus ist nur auf Twenty-Instanzen verfügbar, die im Entwicklungsmodus laufen (`NODE_ENV=development`). Produktionsinstanzen lehnen Dev-Sync-Anfragen ab — verwenden Sie `yarn twenty deploy`, um auf Produktionsserver bereitzustellen. Siehe [Veröffentlichung](/l/de/developers/extend/apps/operations/publishing).
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Starting from an example
|
||||
## Mit einem Beispiel beginnen
|
||||
|
||||
Use `--example` to start with a more complete project (custom objects, fields, logic functions, front components):
|
||||
Verwenden Sie `--example`, um mit einem vollständigeren Projekt zu starten (benutzerdefinierte Objekte, Felder, Logikfunktionen, Frontend-Komponenten):
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app --example postcard
|
||||
```
|
||||
|
||||
Examples live in [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). You can also scaffold individual entities into an existing project with `yarn twenty add` — see [Scaffolding](/l/de/developers/extend/apps/getting-started/scaffolding).
|
||||
Die Beispiele befinden sich unter [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Sie können auch einzelne Entitäten in einem bestehenden Projekt mit `yarn twenty add` erzeugen — siehe [Scaffolding](/l/de/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
---
|
||||
|
||||
## What you can build
|
||||
## Was Sie erstellen können
|
||||
|
||||
Apps are composed of **entities** — each defined as a TypeScript file with a single `export default`:
|
||||
Apps bestehen aus **Entitäten** — jede ist als TypeScript-Datei mit einem einzigen `export default` definiert:
|
||||
|
||||
| Entity | What it does |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------- |
|
||||
| **Objects & Fields** | Custom data models (Post Card, Invoice, etc.) with typed fields |
|
||||
| **Logic functions** | Server-side TypeScript triggered by HTTP routes, cron schedules, or database events |
|
||||
| **Front components** | React components that render inside Twenty's UI (side panel, widgets, command menu) |
|
||||
| **Skills & Agents** | AI capabilities — reusable instructions and autonomous assistants |
|
||||
| **Views & Navigation** | Pre-configured list views and sidebar menu items |
|
||||
| **Page layouts** | Custom record detail pages with tabs and widgets |
|
||||
| Entität | Was es tut |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| **Objekte & Felder** | Benutzerdefinierte Datenmodelle (Postkarte, Rechnung usw.) mit typisierten Feldern |
|
||||
| **Logikfunktionen** | Serverseitiges TypeScript, ausgelöst durch HTTP-Routen, Cron-Zeitpläne oder Datenbankereignisse |
|
||||
| **Frontend-Komponenten** | React-Komponenten, die in der UI von Twenty gerendert werden (Seitenleiste, Widgets, Befehlsmenü) |
|
||||
| **Fähigkeiten & Agenten** | KI-Funktionen — wiederverwendbare Anweisungen und autonome Assistenten |
|
||||
| **Ansichten & Navigation** | Vorkonfigurierte Listenansichten und Seitenleisteneinträge |
|
||||
| **Seitenlayouts** | Benutzerdefinierte Datensatz-Detailseiten mit Tabs und Widgets |
|
||||
|
||||
Full reference: [Concepts](/l/de/developers/extend/apps/getting-started/concepts).
|
||||
Vollständige Referenz: [Konzepte](/l/de/developers/extend/apps/getting-started/concepts).
|
||||
|
||||
## Next steps
|
||||
## Nächste Schritte
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/de/developers/extend/apps/config/overview">
|
||||
Application identity, default role, install hooks, public assets.
|
||||
<Card title="Konfiguration" icon="screwdriver-wrench" href="/l/de/developers/extend/apps/config/overview">
|
||||
Anwendungsidentität, Standardrolle, Install-Hooks, öffentliche Assets.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/de/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
<Card title="Daten" icon="database" href="/l/de/developers/extend/apps/data/overview">
|
||||
Objekte, Felder und bidirektionale Relationen.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/de/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Logik" icon="bolt" href="/l/de/developers/extend/apps/logic/overview">
|
||||
Logikfunktionen, Skills, Agents und OAuth-Verbindungen.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/de/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Ansichten, Navigation, Seiten-Layouts, Front-Komponenten.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/de/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Operationen" icon="rocket" href="/l/de/developers/extend/apps/operations/overview">
|
||||
CLI, Tests, Remotes, CI und die Veröffentlichung Ihrer App.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
title: Scaffolding
|
||||
description: Generate entity files interactively with yarn twenty add — objects, fields, views, logic functions, and more.
|
||||
title: Gerüst erstellen
|
||||
description: Generiere Entitätsdateien interaktiv mit yarn twenty add – Objekte, Felder, Ansichten, Logikfunktionen und mehr.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Instead of creating entity files by hand, use the interactive scaffolder:
|
||||
Anstatt Entitätsdateien manuell zu erstellen, können Sie den interaktiven Scaffolder verwenden:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add
|
||||
```
|
||||
|
||||
It prompts you to pick an entity type and walks you through the required fields, then writes a ready-to-use file with a stable `universalIdentifier` and the correct `defineEntity()` call.
|
||||
Er fordert Sie auf, einen Entitätstyp auszuwählen, führt Sie durch die erforderlichen Felder und schreibt anschließend eine einsatzbereite Datei mit einem stabilen `universalIdentifier` und dem korrekten `defineEntity()`-Aufruf.
|
||||
|
||||
You can also pass the entity type directly to skip the first prompt:
|
||||
Sie können den Entitätstyp auch direkt übergeben, um die erste Eingabeaufforderung zu überspringen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add object
|
||||
@@ -20,38 +20,38 @@ yarn twenty add logicFunction
|
||||
yarn twenty add frontComponent
|
||||
```
|
||||
|
||||
## Available entity types
|
||||
## Verfügbare Entitätstypen
|
||||
|
||||
| Entity type | Command | Generated file |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Object | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Field | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Logic function | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Front component | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Role | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Skill | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| View | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Navigation menu item | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Page layout | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
| Entitätstyp | Befehl | Generierte Datei |
|
||||
| ---------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Objekt | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Feld | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Logikfunktion | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Frontend-Komponente | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Rolle | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Skill | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| Ansicht | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Navigationsmenüeintrag | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Seitenlayout | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
|
||||
## What the scaffolder generates
|
||||
## Was der Scaffolder generiert
|
||||
|
||||
Each entity type has its own template. For example, `yarn twenty add object` asks for:
|
||||
Jeder Entitätstyp hat seine eigene Vorlage. Zum Beispiel fragt `yarn twenty add object` nach:
|
||||
|
||||
1. **Name (singular)** — e.g., `invoice`
|
||||
2. **Name (plural)** — e.g., `invoices`
|
||||
3. **Label (singular)** — auto-populated from the name (e.g., `Invoice`)
|
||||
4. **Label (plural)** — auto-populated (e.g., `Invoices`)
|
||||
5. **Create a view and navigation item?** — if you answer yes, the scaffolder also generates a matching view and sidebar link for the new object.
|
||||
1. **Name (Singular)** — z. B. `invoice`
|
||||
2. **Name (Plural)** — z. B. `invoices`
|
||||
3. **Label (Singular)** — automatisch aus dem Namen befüllt (z. B. `Invoice`)
|
||||
4. **Label (Plural)** — automatisch befüllt (z. B. `Invoices`)
|
||||
5. **Ansicht und Navigationseintrag erstellen?** — wenn Sie mit Ja antworten, erzeugt der Scaffolder außerdem eine passende Ansicht und einen Sidebar-Link für das neue Objekt.
|
||||
|
||||
Other entity types have simpler prompts — most only ask for a name.
|
||||
Andere Entitätstypen haben einfachere Eingabeaufforderungen — die meisten fragen nur nach einem Namen.
|
||||
|
||||
The `field` entity type is more detailed: it asks for the field name, label, type (from a list of all available field types like `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.), and the target object's `universalIdentifier`.
|
||||
Der Entitätstyp `field` ist detaillierter: Er fragt nach Feldname, Label, Typ (aus einer Liste aller verfügbaren Feldtypen wie `TEXT`, `NUMBER`, `SELECT`, `RELATION` usw.) sowie dem `universalIdentifier` des Zielobjekts.
|
||||
|
||||
## Custom output path
|
||||
## Benutzerdefinierter Ausgabepfad
|
||||
|
||||
Use the `--path` flag to place the generated file in a custom location:
|
||||
Verwenden Sie den Schalter `--path`, um die generierte Datei an einem benutzerdefinierten Ort abzulegen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add logicFunction --path src/custom-folder
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Command Menu Items
|
||||
description: Surface front components as quick actions and command menu (Cmd+K) entries with defineCommandMenuItem.
|
||||
icon: terminal
|
||||
title: Befehlsmenü-Einträge
|
||||
description: Stelle Front-Komponenten als Schnellaktionen und Einträge im Befehlsmenü (Cmd+K) mit defineCommandMenuItem bereit.
|
||||
icon: Terminal
|
||||
---
|
||||
|
||||
A **command menu item** is the bridge between the user and a [front component](/l/de/developers/extend/apps/layout/front-components). It registers the component in Twenty's command menu (Cmd+K) and, optionally, as a pinned quick-action button in the top-right corner of the page.
|
||||
Ein **Befehlsmenü-Eintrag** ist die Brücke zwischen dem Benutzer und einer [Front-Komponente](/l/de/developers/extend/apps/layout/front-components). Er registriert die Komponente im Twenty-Befehlsmenü (Cmd+K) und optional als angeheftete Schnellaktions-Schaltfläche in der oberen rechten Ecke der Seite.
|
||||
|
||||
```ts src/command-menu-items/open-dashboard.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -20,25 +20,25 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration fields
|
||||
## Konfigurationsfelder
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Yes | Stable unique ID for the command |
|
||||
| `label` | Yes | Full label shown in the command menu (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Yes | The `universalIdentifier` of the front component this command opens |
|
||||
| `shortLabel` | No | Shorter label displayed on the pinned quick-action button |
|
||||
| `icon` | No | Icon name displayed next to the label (e.g. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | No | When `true`, shows the command as a quick-action button in the top-right corner of the page |
|
||||
| `availabilityType` | No | Controls where the command appears: `'GLOBAL'` (always available), `'RECORD_SELECTION'` (only when records are selected), or `'FALLBACK'` (shown when no other commands match) |
|
||||
| `availabilityObjectUniversalIdentifier` | No | Restrict the command to pages of a specific object type (e.g. only on Company records) |
|
||||
| `conditionalAvailabilityExpression` | No | A boolean expression that dynamically controls visibility (see below) |
|
||||
| Feld | Erforderlich | Beschreibung |
|
||||
| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Ja | Stabile eindeutige ID für den Befehl |
|
||||
| `label` | Ja | Vollständiges Label, das im Befehlsmenü (Cmd+K) angezeigt wird |
|
||||
| `frontComponentUniversalIdentifier` | Ja | Der `universalIdentifier` der Front-Komponente, die dieser Befehl öffnet |
|
||||
| `shortLabel` | Nein | Kürzeres Label, das auf der angehefteten Schnellaktionsschaltfläche angezeigt wird |
|
||||
| `icon` | Nein | Neben dem Label angezeigter Icon-Name (z. B. 'IconBolt', 'IconSend') |
|
||||
| `isPinned` | Nein | Bei `true` wird der Befehl als Schnellaktionsschaltfläche oben rechts auf der Seite angezeigt |
|
||||
| `availabilityType` | Nein | Steuert, wo der Befehl erscheint: 'GLOBAL' (immer verfügbar), 'RECORD_SELECTION' (nur wenn Datensätze ausgewählt sind) oder 'FALLBACK' (wird angezeigt, wenn keine anderen Befehle passen) |
|
||||
| `availabilityObjectUniversalIdentifier` | Nein | Beschränken Sie den Befehl auf Seiten eines bestimmten Objekttyps (z. B. nur bei Company-Datensätzen) |
|
||||
| `conditionalAvailabilityExpression` | Nein | Ein boolescher Ausdruck, der die Sichtbarkeit dynamisch steuert (siehe unten) |
|
||||
|
||||
## Headless commands
|
||||
## Headless-Befehle
|
||||
|
||||
A command menu item paired with a [headless front component](/l/de/developers/extend/apps/layout/front-components#headless-vs-non-headless) is the idiomatic way to ship a one-click action — run code, navigate, or confirm and execute. The Front Components page covers the [SDK Command components](/l/de/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) that handle the action-and-unmount pattern.
|
||||
Ein Befehlsmenü-Eintrag, der mit einer [Headless-Front-Komponente](/l/de/developers/extend/apps/layout/front-components#headless-vs-non-headless) gekoppelt ist, ist die idiomatische Art, eine One-Click-Aktion bereitzustellen – Code ausführen, navigieren oder bestätigen und ausführen. Die Seite „Front Components" behandelt die [SDK Command-Komponenten](/l/de/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`), die das Action-and-Unmount-Muster handhaben.
|
||||
|
||||
A typical flow:
|
||||
Ein typischer Ablauf:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -79,9 +79,9 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Conditional availability expressions
|
||||
## Bedingte Verfügbarkeitsausdrücke
|
||||
|
||||
The `conditionalAvailabilityExpression` field lets you control when a command is visible based on the current page context. Import typed variables and operators from `twenty-sdk` to build expressions:
|
||||
Mit dem Feld `conditionalAvailabilityExpression` können Sie basierend auf dem aktuellen Seitenkontext steuern, wann ein Befehl sichtbar ist. Importieren Sie typisierte Variablen und Operatoren aus `twenty-sdk`, um Ausdrücke zu erstellen:
|
||||
|
||||
```ts src/command-menu-items/bulk-update.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -103,42 +103,42 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
### Context variables
|
||||
### Kontextvariablen
|
||||
|
||||
These represent the current state of the page:
|
||||
Diese repräsentieren den aktuellen Zustand der Seite:
|
||||
|
||||
| Variable | Type | Description |
|
||||
| ------------------------------ | --------- | ---------------------------------------------------------------- |
|
||||
| `pageType` | `string` | Current page type (e.g. `'RecordIndexPage'`, `'RecordShowPage'`) |
|
||||
| `isInSidePanel` | `boolean` | Whether the component is rendered in a side panel |
|
||||
| `numberOfSelectedRecords` | `number` | Number of currently selected records |
|
||||
| `isSelectAll` | `boolean` | Whether "select all" is active |
|
||||
| `selectedRecords` | `array` | The selected record objects |
|
||||
| `favoriteRecordIds` | `array` | IDs of favorited records |
|
||||
| `objectPermissions` | `object` | Permissions for the current object type |
|
||||
| `targetObjectReadPermissions` | `object` | Read permissions for the target object |
|
||||
| `targetObjectWritePermissions` | `object` | Write permissions for the target object |
|
||||
| `featureFlags` | `object` | Active feature flags |
|
||||
| `objectMetadataItem` | `object` | Metadata of the current object type |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Whether the current view has a soft-delete filter |
|
||||
| Variable | Typ | Beschreibung |
|
||||
| ------------------------------ | -------------- | --------------------------------------------------------------- |
|
||||
| `pageType` | `Zeichenkette` | Aktueller Seitentyp (z. B. 'RecordIndexPage', 'RecordShowPage') |
|
||||
| `isInSidePanel` | `boolean` | Ob die Komponente in einem Seitenpanel gerendert wird |
|
||||
| `numberOfSelectedRecords` | `number` | Anzahl der aktuell ausgewählten Datensätze |
|
||||
| `isSelectAll` | `boolean` | Ob „Alle auswählen“ aktiv ist |
|
||||
| `selectedRecords` | `array` | Die ausgewählten Datensatzobjekte |
|
||||
| `favoriteRecordIds` | `array` | IDs der favorisierten Datensätze |
|
||||
| `objectPermissions` | `object` | Berechtigungen für den aktuellen Objekttyp |
|
||||
| `targetObjectReadPermissions` | `object` | Leseberechtigungen für das Zielobjekt |
|
||||
| `targetObjectWritePermissions` | `object` | Schreibberechtigungen für das Zielobjekt |
|
||||
| `featureFlags` | `object` | Aktive Feature-Flags |
|
||||
| `objectMetadataItem` | `object` | Metadaten des aktuellen Objekttyps |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Ob die aktuelle Ansicht einen Soft-Delete-Filter hat |
|
||||
|
||||
### Operators
|
||||
### Operatoren
|
||||
|
||||
Combine variables into boolean expressions:
|
||||
Kombiniere Variablen zu booleschen Ausdrücken:
|
||||
|
||||
| Operator | Description |
|
||||
| ----------------------------------- | ----------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true` if the value is not null/undefined |
|
||||
| `isNonEmptyString(value)` | `true` if the value is a non-empty string |
|
||||
| `includes(array, value)` | `true` if the array contains the value |
|
||||
| `includesEvery(array, prop, value)` | `true` if every item's property includes the value |
|
||||
| `every(array, prop)` | `true` if the property is truthy on every item |
|
||||
| `everyDefined(array, prop)` | `true` if the property is defined on every item |
|
||||
| `everyEquals(array, prop, value)` | `true` if the property equals the value on every item |
|
||||
| `some(array, prop)` | `true` if the property is truthy on at least one item |
|
||||
| `someDefined(array, prop)` | `true` if the property is defined on at least one item |
|
||||
| `someEquals(array, prop, value)` | `true` if the property equals the value on at least one item |
|
||||
| `someNonEmptyString(array, prop)` | `true` if the property is a non-empty string on at least one item |
|
||||
| `none(array, prop)` | `true` if the property is falsy on every item |
|
||||
| `noneDefined(array, prop)` | `true` if the property is undefined on every item |
|
||||
| `noneEquals(array, prop, value)` | `true` if the property does not equal the value on any item |
|
||||
| Operator | Beschreibung |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true`, wenn der Wert nicht null/undefined ist |
|
||||
| `isNonEmptyString(value)` | `true`, wenn der Wert eine nicht leere Zeichenfolge ist |
|
||||
| `includes(array, value)` | `true`, wenn das Array den Wert enthält |
|
||||
| `includesEvery(array, prop, value)` | `true`, wenn die Eigenschaft jedes Elements den Wert enthält |
|
||||
| `every(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element truthy ist |
|
||||
| `everyDefined(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element definiert ist |
|
||||
| `everyEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei jedem Element dem Wert entspricht |
|
||||
| `some(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element truthy ist |
|
||||
| `someDefined(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element definiert ist |
|
||||
| `someEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei mindestens einem Element dem Wert entspricht |
|
||||
| `someNonEmptyString(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element eine nicht leere Zeichenfolge ist |
|
||||
| `none(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element falsy ist |
|
||||
| `noneDefined(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element undefined ist |
|
||||
| `noneEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei keinem Element dem Wert entspricht |
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
---
|
||||
title: Front Components
|
||||
description: Build React components that render inside Twenty's UI with sandboxed isolation.
|
||||
title: Frontend-Komponenten
|
||||
description: Erstellen Sie React-Komponenten, die innerhalb der Twenty-UI gerendert werden und durch eine Sandbox isoliert sind.
|
||||
icon: window-maximize
|
||||
---
|
||||
|
||||
Front components are React components that render directly inside Twenty's UI. They run in an **isolated Web Worker** using Remote DOM — your code is sandboxed but renders natively in the page, not in an iframe.
|
||||
Front-Komponenten sind React-Komponenten, die direkt innerhalb der Twenty-UI gerendert werden. Sie laufen in einem **isolierten Web Worker** unter Verwendung von Remote DOM — Ihr Code wird in einer Sandbox ausgeführt, rendert jedoch nativ auf der Seite, nicht in einem iframe.
|
||||
|
||||
## Where front components can be used
|
||||
## Wo Front-Komponenten verwendet werden können
|
||||
|
||||
Front components can render in two locations within Twenty:
|
||||
Front-Komponenten können an zwei Stellen innerhalb von Twenty gerendert werden:
|
||||
|
||||
* **Side panel** — Non-headless front components open in the right-hand side panel. This is the default behavior when a front component is triggered from the command menu.
|
||||
* **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside [page layouts](/l/de/developers/extend/apps/layout/page-layouts). When configuring a dashboard or a record page layout, users can add a front component widget.
|
||||
* **Seitenpanel** — Nicht-Headless-Front-Komponenten werden im rechten Seitenpanel geöffnet. Dies ist das Standardverhalten, wenn eine Front-Komponente über das Befehlsmenü ausgelöst wird.
|
||||
* **Widgets (Dashboards und Datensatzseiten)** — Front-Komponenten können als Widgets in [Seitenlayouts](/l/de/developers/extend/apps/layout/page-layouts) eingebettet werden. Beim Konfigurieren eines Dashboards oder eines Datensatzseiten-Layouts können Benutzer ein Front-Komponenten-Widget hinzufügen.
|
||||
|
||||
A front component on its own isn't reachable from the UI — you need to *surface* it. The two ways to do that are:
|
||||
Eine Front-Komponente allein ist über die Benutzeroberfläche nicht erreichbar – Sie müssen sie *sichtbar machen*. Die beiden Möglichkeiten dafür sind:
|
||||
|
||||
* **Pair it with a [command menu item](/l/de/developers/extend/apps/layout/command-menu-items)** — registers it in the command menu (Cmd+K) and, optionally, as a pinned quick-action.
|
||||
* **Embed it as a widget in a [page layout](/l/de/developers/extend/apps/layout/page-layouts)** — places it on a record's detail page or dashboard.
|
||||
* **Mit einem [Befehlsmenüeintrag](/l/de/developers/extend/apps/layout/command-menu-items) verknüpfen** — registriert sie im Befehlsmenü (Cmd+K) und optional als angeheftete Schnellaktion.
|
||||
* **Als Widget in ein [Seitenlayout](/l/de/developers/extend/apps/layout/page-layouts) einbetten** — platziert es auf der Detailseite eines Datensatzes oder in einem Dashboard.
|
||||
|
||||
## Basic example
|
||||
## Einfaches Beispiel
|
||||
|
||||
The quickest way to see a front component in action is to pair it with a [`defineCommandMenuItem`](/l/de/developers/extend/apps/layout/command-menu-items), so it appears as a quick-action button in the top-right corner of the page:
|
||||
Die schnellste Möglichkeit, eine Front-Komponente in Aktion zu sehen, besteht darin, sie mit einem [`defineCommandMenuItem`](/l/de/developers/extend/apps/layout/command-menu-items) zu verknüpfen, sodass sie als Schnellaktionsschaltfläche in der oberen rechten Ecke der Seite erscheint:
|
||||
|
||||
```tsx src/front-components/hello-world.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -56,35 +56,35 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
After syncing with `yarn twenty dev` (or running a one-shot `yarn twenty dev --once`), the quick action appears in the top-right corner of the page:
|
||||
Nach dem Synchronisieren mit `yarn twenty dev` (oder durch einmaliges Ausführen von `yarn twenty dev --once`) erscheint die Schnellaktion oben rechts auf der Seite:
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Quick action button in the top-right corner" />
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Schnellaktionsschaltfläche oben rechts" />
|
||||
</div>
|
||||
|
||||
Click it to render the component inline.
|
||||
Klicken Sie darauf, um die Komponente inline zu rendern.
|
||||
|
||||
## Configuration fields
|
||||
## Konfigurationsfelder
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------------- | -------- | ------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Yes | Stable unique ID for this component |
|
||||
| `component` | Yes | A React component function |
|
||||
| `name` | No | Display name |
|
||||
| `description` | No | Description of what the component does |
|
||||
| `isHeadless` | No | Set to `true` if the component has no visible UI (see below) |
|
||||
| Feld | Erforderlich | Beschreibung |
|
||||
| --------------------- | ------------ | --------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Ja | Stabile eindeutige ID für diese Komponente |
|
||||
| `component` | Ja | Eine React-Komponentenfunktion |
|
||||
| `name` | Nein | Anzeigename |
|
||||
| `description` | Nein | Beschreibung dessen, was die Komponente macht |
|
||||
| `isHeadless` | Nein | Auf `true` setzen, wenn die Komponente keine sichtbare UI hat (siehe unten) |
|
||||
|
||||
## Placing a front component on a page
|
||||
## Eine Front-Komponente auf einer Seite platzieren
|
||||
|
||||
Beyond commands, you can embed a front component directly into a record page by adding it as a widget in a **page layout**. See [Page Layouts](/l/de/developers/extend/apps/layout/page-layouts) for details.
|
||||
Über Befehle hinaus können Sie eine Front-Komponente direkt in eine Datensatzseite einbetten, indem Sie sie als Widget in einem **Seitenlayout** hinzufügen. Details finden Sie unter [Seitenlayouts](/l/de/developers/extend/apps/layout/page-layouts).
|
||||
|
||||
## Headless vs non-headless
|
||||
## Headless vs. Nicht-Headless
|
||||
|
||||
Front components come in two rendering modes controlled by the `isHeadless` option:
|
||||
Front-Komponenten gibt es in zwei Rendering-Modi, die durch die Option `isHeadless` gesteuert werden:
|
||||
|
||||
**Non-headless (default)** — The component renders a visible UI. When triggered from the command menu it opens in the side panel. This is the default behavior when `isHeadless` is `false` or omitted.
|
||||
**Nicht-Headless (Standard)** — Die Komponente rendert eine sichtbare UI. Wird sie über das Befehlsmenü ausgelöst, öffnet sie sich im Seitenpanel. Dies ist das Standardverhalten, wenn `isHeadless` `false` ist oder weggelassen wird.
|
||||
|
||||
**Headless (`isHeadless: true`)** — The component mounts invisibly in the background. It does not open the side panel. Headless components are designed for actions that execute logic and then unmount themselves — for example, running an async task, navigating to a page, or showing a confirmation modal. They pair naturally with the SDK Command components described below.
|
||||
**Headless (`isHeadless: true`)** — Die Komponente wird unsichtbar im Hintergrund gemountet. Sie öffnet das Seitenpanel nicht. Headless-Komponenten sind für Aktionen konzipiert, die Logik ausführen und sich anschließend selbst unmounten — zum Beispiel das Ausführen einer asynchronen Aufgabe, das Navigieren zu einer Seite oder das Anzeigen eines Bestätigungsdialogs. Sie lassen sich gut mit den unten beschriebenen SDK-Command-Komponenten kombinieren.
|
||||
|
||||
```tsx src/front-components/sync-tracker.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -110,20 +110,20 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
Because the component returns `null`, Twenty skips rendering a container for it — no empty space appears in the layout. The component still has access to all hooks and the host communication API.
|
||||
Da die Komponente `null` zurückgibt, überspringt Twenty das Rendern eines Containers dafür — im Layout entsteht kein Leerraum. Die Komponente hat dennoch Zugriff auf alle Hooks und die Host-Kommunikations-API.
|
||||
|
||||
## SDK Command components
|
||||
## SDK-Command-Komponenten
|
||||
|
||||
The `twenty-sdk` package provides four Command helper components designed for headless front components. Each component executes an action on mount, handles errors by showing a snackbar notification, and automatically unmounts the front component when done.
|
||||
Das Paket `twenty-sdk` stellt vier Command-Hilfskomponenten bereit, die für Headless-Front-Komponenten ausgelegt sind. Jede Komponente führt beim Mounten eine Aktion aus, behandelt Fehler durch Anzeige einer Snackbar-Benachrichtigung und unmountet die Front-Komponente nach Abschluss automatisch.
|
||||
|
||||
Import them from `twenty-sdk/command`:
|
||||
Importieren Sie sie aus `twenty-sdk/command`:
|
||||
|
||||
* **`Command`** — Runs an async callback via the `execute` prop.
|
||||
* **`CommandLink`** — Navigates to an app path. Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — Opens a confirmation modal. If the user confirms, executes the `execute` callback. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — Opens a specific side panel page. Props: `page`, `pageTitle`, `pageIcon`.
|
||||
* **`Command`** — Führt einen asynchronen Callback über das Prop `execute` aus.
|
||||
* **`CommandLink`** — Navigiert zu einem App-Pfad. Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — Öffnet einen Bestätigungsdialog. Bestätigt der Benutzer, wird der Callback `execute` ausgeführt. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — Öffnet eine bestimmte Seite im Seitenpanel. Props: `page`, `pageTitle`, `pageIcon`.
|
||||
|
||||
Here is a full example of a headless front component using `Command` to run an action from the command menu:
|
||||
Hier ist ein vollständiges Beispiel einer Headless-Front-Komponente, die `Command` verwendet, um eine Aktion aus dem Befehlsmenü auszuführen:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -165,7 +165,7 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
And an example using `CommandModal` to ask for confirmation before executing:
|
||||
Und ein Beispiel, das `CommandModal` verwendet, um vor der Ausführung um Bestätigung zu bitten:
|
||||
|
||||
```tsx src/front-components/delete-draft.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -196,9 +196,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
## Accessing runtime context
|
||||
## Zugriff auf den Laufzeitkontext
|
||||
|
||||
Inside your component, use SDK hooks to access the current user, record, and component instance:
|
||||
Verwenden Sie innerhalb Ihrer Komponente SDK-Hooks, um auf den aktuellen Benutzer, den Datensatz und die Komponenteninstanz zuzugreifen:
|
||||
|
||||
```tsx src/front-components/record-info.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -229,31 +229,31 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
Available hooks:
|
||||
Verfügbare Hooks:
|
||||
|
||||
| Hook | Returns | Description |
|
||||
| --------------------------------------------- | ------------------ | ---------------------------------------------------------- |
|
||||
| `useUserId()` | `string` or `null` | The current user's ID |
|
||||
| `useSelectedRecordIds()` | `string[]` | All selected record IDs (empty array if none selected) |
|
||||
| `useRecordId()` | `string` or `null` | **Deprecated.** Use `useSelectedRecordIds()` instead |
|
||||
| `useFrontComponentId()` | `string` | This component instance's ID |
|
||||
| `useFrontComponentExecutionContext(selector)` | varies | Access the full execution context with a selector function |
|
||||
| Hook | Gibt zurück | Beschreibung |
|
||||
| --------------------------------------------- | -------------------- | --------------------------------------------------------------------------- |
|
||||
| `useUserId()` | `string` oder `null` | Die ID des aktuellen Benutzers |
|
||||
| `useSelectedRecordIds()` | `Zeichenkette[]` | Alle ausgewählten Datensatz-IDs (leeres Array, wenn keine ausgewählt sind) |
|
||||
| `useRecordId()` | `string` oder `null` | **Veraltet.** Verwenden Sie stattdessen `useSelectedRecordIds()` |
|
||||
| `useFrontComponentId()` | `Zeichenkette` | Die ID dieser Komponenteninstanz |
|
||||
| `useFrontComponentExecutionContext(selector)` | variiert | Zugriff auf den vollständigen Ausführungskontext mit einer Selektorfunktion |
|
||||
|
||||
## Host communication API
|
||||
## Host-Kommunikations-API
|
||||
|
||||
Front components can trigger navigation, modals, and notifications using functions from `twenty-sdk`:
|
||||
Front-Komponenten können Navigation, Modals und Benachrichtigungen mittels Funktionen aus `twenty-sdk` auslösen:
|
||||
|
||||
| Function | Description |
|
||||
| ----------------------------------------------- | ----------------------------- |
|
||||
| `navigate(to, params?, queryParams?, options?)` | Navigate to a page in the app |
|
||||
| `openSidePanelPage(params)` | Open a side panel |
|
||||
| `closeSidePanel()` | Close the side panel |
|
||||
| `openCommandConfirmationModal(params)` | Show a confirmation dialog |
|
||||
| `enqueueSnackbar(params)` | Show a toast notification |
|
||||
| `unmountFrontComponent()` | Unmount the component |
|
||||
| `updateProgress(progress)` | Update a progress indicator |
|
||||
| Funktion | Beschreibung |
|
||||
| ----------------------------------------------- | ----------------------------------------- |
|
||||
| `navigate(to, params?, queryParams?, options?)` | Zu einer Seite in der App navigieren |
|
||||
| `openSidePanelPage(params)` | Ein Seitenpanel öffnen |
|
||||
| `closeSidePanel()` | Seitenpanel schließen |
|
||||
| `openCommandConfirmationModal(params)` | Einen Bestätigungsdialog anzeigen |
|
||||
| `enqueueSnackbar(params)` | Eine Toast-Benachrichtigung anzeigen |
|
||||
| `unmountFrontComponent()` | Die Komponente entfernen |
|
||||
| `updateProgress(progress)` | Einen Fortschrittsindikator aktualisieren |
|
||||
|
||||
Here is an example that uses the host API to show a snackbar and close the side panel after an action completes:
|
||||
Hier ist ein Beispiel, das die Host-API verwendet, um nach Abschluss einer Aktion eine Snackbar anzuzeigen und das Seitenpanel zu schließen:
|
||||
|
||||
```tsx src/front-components/archive-record.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -298,9 +298,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
### Working with multiple records
|
||||
### Mit mehreren Datensätzen arbeiten
|
||||
|
||||
Use `useSelectedRecordIds()` to handle multiple selected records. This is useful for bulk operations:
|
||||
Verwenden Sie `useSelectedRecordIds()`, um mehrere ausgewählte Datensätze zu verwalten. Dies ist nützlich für Stapelvorgänge:
|
||||
|
||||
```tsx src/front-components/bulk-export.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -353,9 +353,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
## Public assets
|
||||
## Öffentliche Assets
|
||||
|
||||
Front components can access files from the app's `public/` directory using `getPublicAssetUrl`:
|
||||
Front-Komponenten können mit `getPublicAssetUrl` auf Dateien aus dem `public/`-Verzeichnis der App zugreifen:
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -369,18 +369,18 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
See the [public assets section](/l/de/developers/extend/apps/config/public-assets) for details.
|
||||
Details finden Sie im Abschnitt [Öffentliche Assets](/l/de/developers/extend/apps/config/public-assets).
|
||||
|
||||
## Styling
|
||||
|
||||
Front components support multiple styling approaches. You can use:
|
||||
Front-Komponenten unterstützen mehrere Styling-Ansätze. Sie können verwenden:
|
||||
|
||||
* **Inline styles** — `style={{ color: 'red' }}`
|
||||
* **Twenty UI components** — import from `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar, and more)
|
||||
* **Emotion** — CSS-in-JS with `@emotion/react`
|
||||
* **Styled-components** — `styled.div` patterns
|
||||
* **Tailwind CSS** — utility classes
|
||||
* **Any CSS-in-JS library** compatible with React
|
||||
* **Inline-Styles** — `style={{ color: 'red' }}`
|
||||
* **Twenty-UI-Komponenten** — Import aus `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar und mehr)
|
||||
* **Emotion** — CSS-in-JS mit `@emotion/react`
|
||||
* **Styled-components** — `styled.div`-Muster
|
||||
* **Tailwind CSS** — Utility-Klassen
|
||||
* **Beliebige CSS-in-JS-Bibliothek**, die mit React kompatibel ist
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Navigation Menu Items
|
||||
description: Add custom entries to the workspace sidebar — links to saved views or external URLs.
|
||||
title: Navigationsmenüeinträge
|
||||
description: Fügen Sie der Arbeitsbereichsseitenleiste benutzerdefinierte Einträge hinzu – Links zu gespeicherten Ansichten oder externen URLs.
|
||||
icon: Balken
|
||||
---
|
||||
|
||||
A **navigation menu item** is an entry in the left sidebar. Use `defineNavigationMenuItem()` to ship custom sidebar links — typically one per [view](/l/de/developers/extend/apps/layout/views) you ship — or to point at external URLs.
|
||||
Ein **Navigationsmenüeintrag** ist ein Eintrag in der linken Seitenleiste. Verwenden Sie `defineNavigationMenuItem()`, um benutzerdefinierte Seitenleistenlinks bereitzustellen – typischerweise einen pro [Ansicht](/l/de/developers/extend/apps/layout/views), die Sie bereitstellen – oder um auf externe URLs zu verweisen.
|
||||
|
||||
```ts src/navigation-menu-items/example-navigation-menu-item.ts
|
||||
import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define';
|
||||
@@ -23,22 +23,22 @@ export default defineNavigationMenuItem({
|
||||
|
||||
## Hauptpunkte
|
||||
|
||||
* `type` determines what the menu item links to. Each type pairs with a specific identifier field:
|
||||
* `type` legt fest, worauf der Menüeintrag verweist. Jeder Typ ist einem bestimmten Bezeichnerfeld zugeordnet:
|
||||
|
||||
| Typ | Was es tut | Required field |
|
||||
| ------------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | Opens a saved view | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Opens an external URL | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Groups nested items under a label | `name` (and child items reference the folder via `folderUniversalIdentifier`) |
|
||||
| `NavigationMenuItemType.OBJECT` | Opens an object's default index page | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Opens a standalone page layout | `pageLayoutUniversalIdentifier` |
|
||||
| Typ | Was es tut | Pflichtfeld |
|
||||
| ------------------------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | Öffnet eine gespeicherte Ansicht | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Öffnet eine externe URL | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Gruppiert verschachtelte Einträge unter einer Bezeichnung | `name` (und untergeordnete Einträge verweisen über `folderUniversalIdentifier` auf den Ordner) |
|
||||
| `NavigationMenuItemType.OBJECT` | Öffnet die Standardindexseite eines Objekts | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Öffnet ein eigenständiges Seitenlayout | `pageLayoutUniversalIdentifier` |
|
||||
|
||||
* `position` controls ordering in the sidebar.
|
||||
* `position` steuert die Reihenfolge in der Seitenleiste.
|
||||
|
||||
* `icon` and `color` are optional and customize how the entry looks.
|
||||
* `icon` und `color` sind optional und passen das Erscheinungsbild des Eintrags an.
|
||||
|
||||
* `folderUniversalIdentifier` is also available on any item to nest it inside a `FOLDER`-type parent.
|
||||
* `folderUniversalIdentifier` ist ebenfalls bei jedem Eintrag verfügbar, um ihn innerhalb eines übergeordneten Elements vom Typ `FOLDER` zu verschachteln.
|
||||
|
||||
<Note>
|
||||
**Common pitfall:** creating an object without an associated view + navigation menu item makes that object invisible to users. Unless it's a technical/internal object, every custom object should have a default view *and* a sidebar entry pointing at it.
|
||||
**Häufige Falle:** Wenn Sie ein Objekt ohne zugehörige Ansicht und Navigationsmenüeintrag erstellen, ist dieses Objekt für Benutzer unsichtbar. Sofern es sich nicht um ein technisches/internes Objekt handelt, sollte jedes benutzerdefinierte Objekt eine Standardansicht *und* einen entsprechenden Eintrag in der Seitenleiste haben.
|
||||
</Note>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Place your app inside Twenty's UI — sidebar entries, saved views, record page tabs, and sandboxed React components.
|
||||
title: Übersicht
|
||||
description: Binden Sie Ihre App in das UI von Twenty ein – Seitleisten-Einträge, gespeicherte Ansichten, Registerkarten auf Datensatzseiten und isolierte React-Komponenten.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A Twenty app's **layout layer** is everything the user sees: where the app surfaces in the sidebar, which list views it ships, how its record detail pages are arranged, and which custom React components render inside those pages.
|
||||
Die **Layout-Ebene** einer Twenty-App umfasst alles, was der Benutzer sieht: wo die App in der Seitenleiste erscheint, welche Listenansichten sie bereitstellt, wie ihre Detailseiten für Datensätze angeordnet sind und welche benutzerdefinierten React-Komponenten innerhalb dieser Seiten gerendert werden.
|
||||
|
||||
```text
|
||||
Sidebar Record list Record detail page
|
||||
@@ -23,34 +23,34 @@ A Twenty app's **layout layer** is everything the user sees: where the app surfa
|
||||
and filters └─────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## In diesem Abschnitt
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Views" icon="list" href="/l/de/developers/extend/apps/layout/views">
|
||||
`defineView` — saved list configurations: visible columns, filters, groups.
|
||||
<Card title="Ansichten" icon="Liste" href="/l/de/developers/extend/apps/layout/views">
|
||||
`defineView` — gespeicherte Listen-Konfigurationen: sichtbare Spalten, Filter, Gruppen.
|
||||
</Card>
|
||||
<Card title="Navigation Menu Items" icon="bars" href="/l/de/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — sidebar entries pointing at views or external URLs.
|
||||
<Card title="Navigationselemente im Menü" icon="Balken" href="/l/de/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — Seitleisten-Einträge, die auf Ansichten oder externe URLs verweisen.
|
||||
</Card>
|
||||
<Card title="Page Layouts" icon="table-columns" href="/l/de/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` and `definePageLayoutTab` — tabs and widgets on a record's detail page.
|
||||
<Card title="Seitenlayouts" icon="table-columns" href="/l/de/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` und `definePageLayoutTab` — Registerkarten und Widgets auf der Detailseite eines Datensatzes.
|
||||
</Card>
|
||||
<Card title="Front Components" icon="window-maximize" href="/l/de/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — sandboxed React components that render inside Twenty.
|
||||
<Card title="Frontend-Komponenten" icon="window-maximize" href="/l/de/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — isolierte React-Komponenten, die innerhalb von Twenty gerendert werden.
|
||||
</Card>
|
||||
<Card title="Command Menu Items" icon="terminal" href="/l/de/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — register front components as Cmd+K entries and quick actions.
|
||||
<Card title="Befehlsmenüeinträge" icon="Terminal" href="/l/de/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — Front-Komponenten als Cmd+K-Einträge und Schnellaktionen registrieren.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Where the app surfaces
|
||||
## Wo die App erscheint
|
||||
|
||||
| Surface | What it controls | Entity |
|
||||
| --------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Sidebar** | A custom entry linking to a saved view or external URL | `defineNavigationMenuItem` |
|
||||
| **Record list** | A saved configuration for an object — visible columns, order, filters, groups | `defineView` |
|
||||
| **Record detail page** | The tabs and widgets on a record page (your own object's, or a standard one) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **Inside any of the above** | A custom React widget — buttons, forms, dashboards, integrations | `defineFrontComponent` |
|
||||
| **Command menu (Cmd+K)** | A pinned quick action or hidden command | `defineCommandMenuItem` |
|
||||
| Oberfläche | Was es steuert | Entität |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Seitenleiste** | Ein benutzerdefinierter Eintrag, der auf eine gespeicherte Ansicht oder eine externe URL verweist | `defineNavigationMenuItem` |
|
||||
| **Datensatzliste** | Eine gespeicherte Konfiguration für ein Objekt – sichtbare Spalten, Reihenfolge, Filter, Gruppen | `defineView` |
|
||||
| **Detailseite des Datensatzes** | Die Registerkarten und Widgets auf einer Datensatzseite (für Ihr eigenes Objekt oder ein Standardobjekt) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **Innerhalb eines der oben genannten Bereiche** | Ein benutzerdefiniertes React-Widget – Schaltflächen, Formulare, Dashboards, Integrationen | `defineFrontComponent` |
|
||||
| **Befehlsmenü (Cmd+K)** | Eine angeheftete Schnellaktion oder ein versteckter Befehl | `defineCommandMenuItem` |
|
||||
|
||||
Front components run inside an isolated Web Worker using Remote DOM — they render *natively* in the page (not inside an iframe), but cannot reach the host page or DOM directly. Communication with Twenty happens through a message-passing host API.
|
||||
Front-Komponenten laufen in einem isolierten Web Worker unter Verwendung von Remote DOM – sie werden *nativ* auf der Seite gerendert (nicht in einem iframe), können aber die Hostseite oder das DOM nicht direkt erreichen. Die Kommunikation mit Twenty erfolgt über eine Message-Passing-Host-API.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Page Layouts
|
||||
description: Customize record detail pages — tabs, widgets, and where front components render — using definePageLayout and definePageLayoutTab.
|
||||
title: Seitenlayouts
|
||||
description: Passen Sie Detailseiten von Datensätzen an – Tabs, Widgets und die Stellen, an denen Frontend-Komponenten gerendert werden – mithilfe von `definePageLayout` und `definePageLayoutTab`.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A **page layout** controls how a record's detail page is arranged: which tabs appear and what widgets they contain. Use `definePageLayout()` to declare a layout for an object you own, or `definePageLayoutTab()` to add a single tab to a layout that already exists (yours or a standard Twenty one).
|
||||
Ein **Seitenlayout** steuert, wie die Detailseite eines Datensatzes angeordnet ist: welche Tabs angezeigt werden und welche Widgets sie enthalten. Verwenden Sie `definePageLayout()`, um ein Layout für ein Objekt zu deklarieren, das Sie besitzen, oder `definePageLayoutTab()`, um einen einzelnen Tab zu einem Layout hinzuzufügen, das bereits existiert (Ihr eigenes oder ein standardmäßiges Twenty-Layout).
|
||||
|
||||
| Use case | Entity |
|
||||
| ---------------------------------------------------------------------- | --------------------- |
|
||||
| Define the entire layout for a record page on an object you own | `definePageLayout` |
|
||||
| Add one tab to an existing layout (your own object, or a standard one) | `definePageLayoutTab` |
|
||||
| Anwendungsfall | Entität |
|
||||
| ----------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| Definieren Sie das gesamte Layout für die Datensatzseite eines Objekts, das Sie besitzen | `definePageLayout` |
|
||||
| Fügen Sie einem vorhandenen Layout einen Tab hinzu (Ihr eigenes Objekt oder ein Standardlayout) | `definePageLayoutTab` |
|
||||
|
||||
## definePageLayout
|
||||
|
||||
Use this when you own the entire detail page — typically for a custom object you defined yourself.
|
||||
Verwenden Sie dies, wenn Sie die gesamte Detailseite besitzen – typischerweise für ein benutzerdefiniertes Objekt, das Sie selbst definiert haben.
|
||||
|
||||
```ts src/page-layouts/example-record-page-layout.ts
|
||||
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
|
||||
@@ -49,17 +49,17 @@ export default definePageLayout({
|
||||
});
|
||||
```
|
||||
|
||||
### Key points
|
||||
### Hauptpunkte
|
||||
|
||||
* `type` is typically `'RECORD_PAGE'` to customize the detail view of a specific object.
|
||||
* `objectUniversalIdentifier` specifies which object this layout applies to.
|
||||
* Each `tab` defines a section of the page with a `title`, `position`, and `layoutMode` (`CANVAS` for free-form layout).
|
||||
* Each `widget` inside a tab can render a [front component](/l/de/developers/extend/apps/layout/front-components), a relation list, or other built-in widget types.
|
||||
* `position` on tabs controls their order. Use higher values (e.g., 50) to place custom tabs after built-in ones.
|
||||
* `type` ist typischerweise `'RECORD_PAGE'`, um die Detailansicht eines bestimmten Objekts anzupassen.
|
||||
* `objectUniversalIdentifier` gibt an, auf welches Objekt dieses Layout angewendet wird.
|
||||
* Jeder `tab` definiert einen Abschnitt der Seite mit `title`, `position` und `layoutMode` (`CANVAS` für ein freies Layout).
|
||||
* Jedes `widget` innerhalb eines Tabs kann eine [Frontend-Komponente](/l/de/developers/extend/apps/layout/front-components), eine Relationenliste oder andere eingebaute Widget-Typen rendern.
|
||||
* `position` auf Tabs steuert deren Reihenfolge. Verwenden Sie höhere Werte (z. B. 50), um benutzerdefinierte Tabs hinter den integrierten zu platzieren.
|
||||
|
||||
## definePageLayoutTab
|
||||
|
||||
Use this when you only want to **add** a tab to an existing layout — for example, an analytics tab on the standard Company page, or an AI summary tab attached to your own object's layout.
|
||||
Verwenden Sie dies, wenn Sie nur einen Tab zu einem vorhandenen Layout **hinzufügen** möchten – zum Beispiel einen Analytics-Tab auf der standardmäßigen Company-Seite oder einen KI-Zusammenfassungs-Tab, der an das Layout Ihres eigenen Objekts angehängt ist.
|
||||
|
||||
```ts src/page-layouts/example-extra-tab.ts
|
||||
import {
|
||||
@@ -94,9 +94,9 @@ export default definePageLayoutTab({
|
||||
});
|
||||
```
|
||||
|
||||
### Key points
|
||||
### Hauptpunkte
|
||||
|
||||
* `pageLayoutUniversalIdentifier` is **required** and must point to a page layout that already exists at install time — either a standard Twenty layout or one defined by your own app. Cross-app references to layouts owned by another installed app are not supported today. When the parent layout is missing, installation fails with a clear validation error.
|
||||
* `widgets` are scoped to this tab only — they reference [front components](/l/de/developers/extend/apps/layout/front-components), views, etc. exactly like widgets defined inline in `definePageLayout`.
|
||||
* `position` controls ordering against existing tabs on the targeted layout. Pick a value that places your tab where you want it relative to built-in tabs.
|
||||
* Use this instead of `definePageLayout` when you only want to add to an existing layout. Use `definePageLayout` when you own the entire layout.
|
||||
* `pageLayoutUniversalIdentifier` ist **erforderlich** und muss auf ein Seitenlayout verweisen, das zum Installationszeitpunkt bereits existiert – entweder ein standardmäßiges Twenty-Layout oder eines, das von Ihrer eigenen App definiert wurde. App-übergreifende Verweise auf Layouts, die einer anderen installierten App gehören, werden derzeit nicht unterstützt. Wenn das übergeordnete Layout fehlt, schlägt die Installation mit einem eindeutigen Validierungsfehler fehl.
|
||||
* `widgets` sind ausschließlich auf diesen Tab beschränkt – sie verweisen auf [Frontend-Komponenten](/l/de/developers/extend/apps/layout/front-components), Ansichten usw., genau wie Widgets, die inline in `definePageLayout` definiert sind.
|
||||
* `position` steuert die Reihenfolge im Zielseitenlayout relativ zu den vorhandenen Registerkarten. Wählen Sie einen Wert, der Ihre Registerkarte relativ zu integrierten Registerkarten an die gewünschte Position bringt.
|
||||
* Verwenden Sie dies anstelle von `definePageLayout`, wenn Sie einem vorhandenen Layout nur etwas hinzufügen möchten. Verwenden Sie `definePageLayout`, wenn Sie das gesamte Layout besitzen.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Views
|
||||
description: Ship pre-configured saved views — column order, filters, groups — for objects in your app.
|
||||
title: Ansichten
|
||||
description: Stellen Sie vorkonfigurierte gespeicherte Ansichten bereit – Spaltenreihenfolge, Filter, Gruppen – für Objekte in Ihrer App.
|
||||
icon: list
|
||||
---
|
||||
|
||||
A **view** is a saved configuration for how records of an object are displayed: which fields appear, their order, whether they're visible, and any filters or groups applied. Use `defineView()` to ship pre-configured views with your app — typically a default index view for each custom object you create.
|
||||
Eine **Ansicht** ist eine gespeicherte Konfiguration dafür, wie Datensätze eines Objekts angezeigt werden: welche Felder erscheinen, in welcher Reihenfolge, ob sie sichtbar sind und welche Filter oder Gruppen angewendet werden. Verwenden Sie `defineView()`, um vorkonfigurierte Ansichten mit Ihrer App auszuliefern – typischerweise eine Standard-Indexansicht für jedes benutzerdefinierte Objekt, das Sie erstellen.
|
||||
|
||||
```ts src/views/example-view.ts
|
||||
import { defineView, ViewKey } from 'twenty-sdk/define';
|
||||
@@ -30,14 +30,14 @@ export default defineView({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Hauptpunkte
|
||||
|
||||
* `objectUniversalIdentifier` specifies which object this view applies to. It can be a custom object you defined or a standard Twenty object.
|
||||
* `key` determines the view type — `ViewKey.INDEX` is the main list view for the object.
|
||||
* `fields` controls which columns appear and in what order. Each field references a `fieldMetadataUniversalIdentifier`.
|
||||
* You can also declare `filters`, `filterGroups`, `groups`, and `fieldGroups` for advanced configurations.
|
||||
* `position` controls ordering when multiple views exist for the same object.
|
||||
* `objectUniversalIdentifier` gibt an, auf welches Objekt diese Ansicht angewendet wird. Es kann sich um ein von Ihnen definiertes benutzerdefiniertes Objekt oder ein Standardobjekt von Twenty handeln.
|
||||
* `key` bestimmt den Ansichtstyp – `ViewKey.INDEX` ist die Hauptlistenansicht für das Objekt.
|
||||
* `fields` steuert, welche Spalten erscheinen und in welcher Reihenfolge. Jedes Feld referenziert einen `fieldMetadataUniversalIdentifier`.
|
||||
* Für erweiterte Konfigurationen können Sie außerdem `filters`, `filterGroups`, `groups` und `fieldGroups` deklarieren.
|
||||
* `position` steuert die Reihenfolge, wenn mehrere Ansichten für dasselbe Objekt existieren.
|
||||
|
||||
## How views show up in the UI
|
||||
## Wie Ansichten in der UI angezeigt werden
|
||||
|
||||
A view by itself isn't reachable from the sidebar. To make it appear there, pair it with a [navigation menu item](/l/de/developers/extend/apps/layout/navigation-menu-items) of type `VIEW` that points at the view's `universalIdentifier`. That's the canonical pattern: every custom object typically ships a default view + a sidebar entry that opens it.
|
||||
Eine Ansicht für sich ist aus der Seitenleiste nicht erreichbar. Damit sie dort erscheint, verknüpfen Sie sie mit einem [Navigationsmenüeintrag](/l/de/developers/extend/apps/layout/navigation-menu-items) des Typs `VIEW`, der auf die `universalIdentifier` der Ansicht zeigt. Das ist das kanonische Muster: Jedes benutzerdefinierte Objekt liefert typischerweise eine Standardansicht plus einen Eintrag in der Seitenleiste, der sie öffnet.
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
title: Connections
|
||||
description: Let your app act on a user's behalf in third-party services via OAuth.
|
||||
title: Verbindungen
|
||||
description: Ermöglichen Sie Ihrer App, im Namen eines Benutzers über OAuth in Diensten von Drittanbietern zu handeln.
|
||||
icon: plug
|
||||
---
|
||||
|
||||
Connections are credentials a user holds for an external service (Linear, GitHub, Slack, ...). Your app declares **how** those credentials are obtained — a **connection provider** — and consumes them at runtime to make authenticated calls to the third-party API.
|
||||
Verbindungen sind Anmeldedaten, die ein Benutzer für einen externen Dienst besitzt (Linear, GitHub, Slack, ...). Ihre App legt fest, **wie** diese Anmeldedaten bezogen werden — ein **Verbindungsanbieter** — und verwendet sie zur Laufzeit, um authentifizierte Aufrufe an die Drittanbieter-API zu tätigen.
|
||||
|
||||
Today only OAuth 2.0 is supported. Future credential types (personal access tokens, API keys, basic auth) will plug into the same surface — apps already using `defineConnectionProvider({ type: 'oauth', ... })` won't need to migrate.
|
||||
Derzeit wird nur OAuth 2.0 unterstützt. Zukünftige Anmeldedatentypen (Personal Access Tokens, API-Schlüssel, Basic Auth) werden in dieselbe Oberfläche integriert — Apps, die bereits `defineConnectionProvider({ type: 'oauth', ... })` müssen nicht migriert werden.
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="defineConnectionProvider" description="Declare how your app's connections are obtained">
|
||||
<Accordion title="defineConnectionProvider" description="Legen Sie fest, wie die Verbindungen Ihrer App bezogen werden">
|
||||
|
||||
A connection provider describes the OAuth handshake your app needs. The user clicks "Add connection" in your app's settings, completes the provider's consent screen, and a `ConnectedAccount` row is created in their workspace.
|
||||
Ein Verbindungsanbieter beschreibt den OAuth-Handshake, den Ihre App benötigt. Der Benutzer klickt in den Einstellungen Ihrer App auf "Verbindung hinzufügen", schließt den Zustimmungsbildschirm des Anbieters ab, und in seinem Arbeitsbereich wird eine `ConnectedAccount`-Zeile erstellt.
|
||||
|
||||
A working setup needs **two files** — the connection provider, and a matching `serverVariables` declaration on `defineApplication` that holds the OAuth client credentials.
|
||||
Eine funktionierende Einrichtung benötigt **zwei Dateien** — den Verbindungsanbieter und eine passende `serverVariables`-Deklaration in `defineApplication`, die die OAuth-Client-Anmeldedaten enthält.
|
||||
|
||||
```ts src/connection-providers/linear-connection.ts
|
||||
import { defineConnectionProvider } from 'twenty-sdk/define';
|
||||
@@ -71,16 +71,16 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Hauptpunkte:
|
||||
|
||||
* `name` is the unique identifier string used in `listConnections({ providerName })` (kebab-case, must match `^[a-z][a-z0-9-]*$`).
|
||||
* `displayName` shows in the per-app settings tab and in the AI tool list.
|
||||
* `clientIdVariable` / `clientSecretVariable` are **names**, not values — they must match keys declared in `defineApplication.serverVariables`. The actual `client_id` and `client_secret` are entered by the server admin through the app registration UI, never committed to your repo.
|
||||
* Use `serverVariables` (not `applicationVariables`) — OAuth credentials are server-wide and one OAuth app per Twenty server.
|
||||
* Until both `serverVariables` are filled in, the per-app settings tab shows a "needs server admin" hint and the "Add connection" button is disabled.
|
||||
* `type: 'oauth'` is the only supported value today. The discriminator is forward-compatible: future types (`'pat'`, `'api-key'`, ...) will add new sub-config blocks alongside `oauth`.
|
||||
* `name` ist die eindeutige Bezeichner-Zeichenfolge, die in `listConnections({ providerName })` verwendet wird (kebab-case, muss `^[a-z][a-z0-9-]*$` entsprechen).
|
||||
* `displayName` wird im Einstellungs-Tab der jeweiligen App und in der KI-Toolliste angezeigt.
|
||||
* `clientIdVariable` / `clientSecretVariable` sind **Namen**, keine Werte — sie müssen den in `defineApplication.serverVariables` deklarierten Schlüsseln entsprechen. Die tatsächlichen `client_id` und `client_secret` werden vom Serveradministrator über die App-Registrierungsoberfläche eingegeben und niemals in Ihr Repository eingecheckt.
|
||||
* Verwenden Sie `serverVariables` (nicht `applicationVariables`) — OAuth-Anmeldedaten gelten serverweit und es gibt eine OAuth-App pro Twenty-Server.
|
||||
* Solange beide `serverVariables` nicht ausgefüllt sind, zeigt der Einstellungs-Tab pro App den Hinweis "Benötigt Server-Admin" an und der Button "Verbindung hinzufügen" ist deaktiviert.
|
||||
* `type: 'oauth'` ist derzeit der einzige unterstützte Wert. Der Diskriminator ist vorwärtskompatibel: zukünftige Typen (`'pat'`, `'api-key'`, ...) werden neue Unterkonfigurationsblöcke neben `oauth` hinzufügen.
|
||||
|
||||
The OAuth callback URL your provider needs to whitelist is:
|
||||
Die OAuth-Callback-URL, die Ihr Anbieter auf die Whitelist setzen muss, lautet:
|
||||
|
||||
```
|
||||
https://<your-twenty-server>/apps/oauth/callback
|
||||
@@ -88,9 +88,9 @@ https://<your-twenty-server>/apps/oauth/callback
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="listConnections / getConnection" description="Use connections from a logic function">
|
||||
<Accordion title="listConnections / getConnection" description="Verbindungen aus einer Logikfunktion verwenden">
|
||||
|
||||
Inside a logic function handler, `listConnections({ providerName })` returns this app's `ConnectedAccount` rows for the given provider, with refreshed access tokens.
|
||||
Innerhalb eines Logikfunktions-Handlers gibt `listConnections({ providerName })` die `ConnectedAccount`-Zeilen dieser App für den angegebenen Anbieter zurück, mit aktualisierten Zugriffstoken.
|
||||
|
||||
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
||||
import { listConnections } from 'twenty-sdk/logic-function';
|
||||
@@ -135,34 +135,34 @@ export const createLinearIssueHandler = async (input: {
|
||||
};
|
||||
```
|
||||
|
||||
Each connection has:
|
||||
Jede Verbindung hat:
|
||||
|
||||
| Field | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Unique row id; pass to `getConnection(id)` to refetch a single one |
|
||||
| `visibility` | `'user'` (private to one workspace member) or `'workspace'` (shared with all members) |
|
||||
| `scopes` | OAuth permissions granted by the upstream provider (distinct from `visibility` — those are unrelated) |
|
||||
| `userWorkspaceId` | The owner's userWorkspace id — useful for picking "the request user's connection" in HTTP-route triggers |
|
||||
| `accessToken` | Fresh OAuth access token (refreshed automatically if expired) |
|
||||
| `name` / `handle` | The connection's display name (auto-derived at OAuth callback, user-renameable) |
|
||||
| `authFailedAt` | Set when the most recent refresh failed; the user must reconnect |
|
||||
| Feld | Beschreibung |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Eindeutige Zeilen-ID; an `getConnection(id)` übergeben, um eine einzelne Verbindung erneut abzurufen |
|
||||
| `sichtbarkeit` | `'user'` (privat für ein Mitglied des Arbeitsbereichs) oder `'workspace'` (mit allen Mitgliedern geteilt) |
|
||||
| `geltungsbereiche` | Vom Upstream-Anbieter gewährte OAuth-Berechtigungen (unabhängig von `visibility` — diese sind nicht miteinander verknüpft) |
|
||||
| `userWorkspaceId` | Die userWorkspace-ID des Eigentümers — nützlich, um "die Verbindung des anfragenden Benutzers" in HTTP-Routen-Triggern auszuwählen |
|
||||
| `accessToken` | Frisches OAuth-Zugriffstoken (wird bei Ablauf automatisch erneuert) |
|
||||
| `name` / `handle` | Anzeigename der Verbindung (automatisch beim OAuth-Callback abgeleitet, vom Benutzer umbenennbar) |
|
||||
| `authFailedAt` | Gesetzt, wenn die jüngste Aktualisierung fehlgeschlagen ist; der Benutzer muss die Verbindung erneut herstellen |
|
||||
|
||||
Key points:
|
||||
Hauptpunkte:
|
||||
|
||||
* Pass `{ providerName }` to filter by provider; omit it to get all connections this app owns across all providers.
|
||||
* The server transparently refreshes the access token before returning. Your handler always sees a usable token (or `authFailedAt` set).
|
||||
* `getConnection(id)` is the single-row equivalent.
|
||||
* Übergeben Sie `{ providerName }`, um nach Anbieter zu filtern; lassen Sie es weg, um alle Verbindungen dieser App über alle Anbieter hinweg zu erhalten.
|
||||
* Der Server aktualisiert das Zugriffstoken vor der Rückgabe transparent. Ihr Handler sieht stets ein verwendbares Token (oder `authFailedAt` ist gesetzt).
|
||||
* `getConnection(id)` ist das Pendant für eine einzelne Zeile.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Per-user vs workspace-shared visibility" description="How users choose between private and shared credentials">
|
||||
<Accordion title="Sichtbarkeit: pro Benutzer vs. im Arbeitsbereich geteilt" description="Wie Benutzer zwischen privaten und geteilten Anmeldedaten wählen">
|
||||
|
||||
When a user clicks "Add connection," they're prompted to pick a visibility:
|
||||
Wenn ein Benutzer auf "Verbindung hinzufügen" klickt, wird er aufgefordert, eine Sichtbarkeit auszuwählen:
|
||||
|
||||
* **Just for me** — the credential is private to the connecting user. Any logic function called on their behalf (HTTP-route trigger with `isAuthRequired: true`) sees it; cron triggers and database events do not.
|
||||
* **Workspace shared** — any workspace member can use the credential. Cron / database triggers also see it, since they have no request user.
|
||||
* **Nur für mich** — die Anmeldedaten sind für den sich verbindenden Benutzer privat. Jede Logikfunktion, die in seinem/ihrem Auftrag aufgerufen wird (HTTP-Routen-Trigger mit `isAuthRequired: true`), sieht sie; Cron-Trigger und Datenbankereignisse nicht.
|
||||
* **Im Arbeitsbereich geteilt** — jedes Arbeitsbereichsmitglied kann die Anmeldedaten verwenden. Cron-/Datenbank-Trigger sehen sie ebenfalls, da sie keinen anfragenden Benutzer haben.
|
||||
|
||||
Use the right one for each handler:
|
||||
Verwenden Sie für jeden Handler die richtige Option:
|
||||
|
||||
```ts
|
||||
// HTTP-route trigger — prefer the request user's own connection.
|
||||
@@ -174,19 +174,19 @@ const conn =
|
||||
const conn = connections.find((c) => c.visibility === 'workspace');
|
||||
```
|
||||
|
||||
Multiple connections per (user, provider) are allowed, so the same user can hold "Personal Linear" and "Work Linear" side by side.
|
||||
Mehrere Verbindungen pro (Benutzer, Anbieter) sind erlaubt, sodass derselbe Benutzer "Persönliches Linear" und "Arbeits-Linear" nebeneinander haben kann.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="One-time provider setup" description="Register your OAuth app with the third-party service">
|
||||
<Accordion title="Einmalige Anbietereinrichtung" description="Registrieren Sie Ihre OAuth-App beim Drittanbieterdienst">
|
||||
|
||||
For each connection provider, the server admin needs to register an OAuth app at the third party first.
|
||||
Für jeden Verbindungsanbieter muss der Serveradministrator zunächst eine OAuth-App beim Drittanbieter registrieren.
|
||||
|
||||
1. Go to the provider's developer settings (e.g. https://linear.app/settings/api/applications/new).
|
||||
2. Set the **Redirect URI** to `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Copy the generated **Client ID** and **Client Secret**.
|
||||
4. Open the installed app in Twenty as a server admin → set the values on the corresponding `serverVariables`.
|
||||
5. Workspace members can then add connections from the per-app **Connections** section.
|
||||
1. Gehen Sie zu den Entwickler-Einstellungen des Anbieters (z. B. https://linear.app/settings/api/applications/new).
|
||||
2. Setzen Sie die **Redirect-URI** auf `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Kopieren Sie die generierte **Client ID** und das **Client Secret**.
|
||||
4. Öffnen Sie die installierte App in Twenty als Serveradministrator → setzen Sie die Werte in den entsprechenden `serverVariables`.
|
||||
5. Mitglieder des Arbeitsbereichs können dann Verbindungen im **Verbindungen**-Abschnitt der jeweiligen App hinzufügen.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Logic Functions
|
||||
description: Define server-side TypeScript functions with HTTP, cron, and database event triggers.
|
||||
title: Logikfunktionen
|
||||
description: Definieren Sie serverseitige TypeScript-Funktionen mit HTTP-, cron- und Datenbankereignis-Triggern.
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
Logic functions are server-side TypeScript functions that run on the Twenty platform. They can be triggered by HTTP requests, cron schedules, or database events — and can also be exposed as tools for AI agents.
|
||||
Logikfunktionen sind serverseitige TypeScript-Funktionen, die auf der Twenty-Plattform ausgeführt werden. Sie können durch HTTP-Anfragen, cron-Zeitpläne oder Datenbankereignisse ausgelöst werden — und außerdem als Tools für KI-Agenten bereitgestellt werden.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineLogicFunction" description="Define logic functions and their triggers">
|
||||
<Accordion title="defineLogicFunction" description="Logikfunktionen und deren Trigger definieren">
|
||||
|
||||
Each function file uses `defineLogicFunction()` to export a configuration with a handler and optional triggers.
|
||||
Jede Funktionsdatei verwendet `defineLogicFunction()`, um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren.
|
||||
|
||||
```ts src/logic-functions/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -50,15 +50,15 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
Available trigger types:
|
||||
* **httpRoute**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
|
||||
> e.g. `path: '/post-card/create'` is callable at `https://your-twenty-server.com/s/post-card/create`
|
||||
* **cron**: Runs your function on a schedule using a CRON expression.
|
||||
* **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
|
||||
> e.g. `person.updated`, `*.created`, `company.*`
|
||||
Verfügbare Trigger-Typen:
|
||||
* **httpRoute**: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode **unter dem Endpunkt `/s/`** bereit:
|
||||
> z. B. `path: '/post-card/create'` ist unter `https://your-twenty-server.com/s/post-card/create` aufrufbar
|
||||
* **cron**: Führt Ihre Funktion nach Zeitplan mithilfe eines CRON-Ausdrucks aus.
|
||||
* **databaseEvent**: Wird bei Lebenszyklusereignissen von Workspace-Objekten ausgeführt. Wenn die Ereignisoperation `updated` ist, können bestimmte zu überwachende Felder im Array `updatedFields` angegeben werden. Wenn das Array undefiniert oder leer ist, löst jede Aktualisierung die Funktion aus.
|
||||
> z. B. `person.updated`, `*.created`, `company.*`
|
||||
|
||||
<Note>
|
||||
You can also manually execute a function using the CLI:
|
||||
Sie können eine Funktion auch manuell über die CLI ausführen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
|
||||
@@ -68,18 +68,17 @@ yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
|
||||
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
You can watch logs with:
|
||||
Sie können Protokolle mit folgendem Befehl ansehen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty logs
|
||||
```
|
||||
</Note>
|
||||
|
||||
#### Route trigger payload
|
||||
#### Routen-Trigger-Payload
|
||||
|
||||
When a route trigger invokes your logic function, it receives a `RoutePayload` object that follows the
|
||||
[AWS HTTP API v2 format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html).
|
||||
Import the `RoutePayload` type from `twenty-sdk`:
|
||||
Wenn ein Route-Trigger Ihre Logikfunktion aufruft, erhält sie ein `RoutePayload`-Objekt, das dem [AWS-HTTP-API-v2-Format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) folgt.
|
||||
Importieren Sie den Typ `RoutePayload` aus `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';
|
||||
@@ -92,24 +91,24 @@ const handler = async (event: RoutePayload) => {
|
||||
};
|
||||
```
|
||||
|
||||
The `RoutePayload` type has the following structure:
|
||||
Der Typ `RoutePayload` hat die folgende Struktur:
|
||||
|
||||
| Property | Type | Description | Example |
|
||||
| ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `headers` | `Record\<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) | see section below |
|
||||
| `queryStringParameters` | `Record\<string, string \| undefined>` | Query string parameters (multiple values joined with commas) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` |
|
||||
| `pathParameters` | `Record\<string, string \| undefined>` | Path parameters extracted from the route pattern | `/users/:id`, `/users/123` -> `{ id: '123' }` |
|
||||
| `body` | `object \| null` | Parsed request body (JSON) | `{ id: 1 }` -> `{ id: 1 }` |
|
||||
| `rawBody` | `string \| undefined` | Original UTF-8 request body, before JSON parsing. Useful for verifying HMAC-style webhook signatures (e.g. GitHub's `X-Hub-Signature-256`, Stripe). `undefined` when the runtime did not preserve it. | |
|
||||
| `isBase64Encoded` | `boolean` | Whether the body is base64 encoded | |
|
||||
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) | |
|
||||
| `requestContext.http.path` | `string` | Raw request path | |
|
||||
| Eigenschaft | Typ | Beschreibung | Beispiel |
|
||||
| ---------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `headers` | `Record\<string, string \| undefined>` | HTTP-Header (nur die in `forwardedRequestHeaders` aufgelisteten) | siehe Abschnitt unten |
|
||||
| `queryStringParameters` | `Record\<string, string \| undefined>` | Query-String-Parameter (mehrere Werte mit Kommas verbunden) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` |
|
||||
| `pathParameters` | `Record\<string, string \| undefined>` | Aus dem Routenmuster extrahierte Pfadparameter | `/users/:id`, `/users/123` -> `{ id: '123' }` |
|
||||
| `body` | `object \| null` | Geparster Request-Body (JSON) | `{ id: 1 }` -> `{ id: 1 }` |
|
||||
| `rawBody` | `string \| undefined` | Ursprünglicher UTF-8-Request-Body vor dem JSON-Parsing. Nützlich zur Verifizierung von Webhook-Signaturen im HMAC-Stil (z. B. GitHubs `X-Hub-Signature-256`, Stripe). `undefined`, wenn die Laufzeitumgebung es nicht beibehalten hat. | |
|
||||
| `isBase64Encoded` | `boolean` | Gibt an, ob der Body Base64-codiert ist | |
|
||||
| `requestContext.http.method` | `string` | HTTP-Methode (GET, POST, PUT, PATCH, DELETE) | |
|
||||
| `requestContext.http.path` | `string` | Rohpfad der Anfrage | |
|
||||
|
||||
|
||||
#### forwardedRequestHeaders
|
||||
|
||||
By default, HTTP headers from incoming requests are **not** passed to your logic function for security reasons.
|
||||
To access specific headers, list them in the `forwardedRequestHeaders` array:
|
||||
Standardmäßig werden HTTP-Header von eingehenden Anfragen aus Sicherheitsgründen nicht an Ihre Logikfunktion weitergegeben.
|
||||
Um auf bestimmte Header zuzugreifen, listen Sie diese im Array `forwardedRequestHeaders` auf:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
@@ -125,7 +124,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
In your handler, access the forwarded headers like this:
|
||||
Greifen Sie in Ihrem Handler wie folgt auf die weitergeleiteten Header zu:
|
||||
|
||||
```ts
|
||||
const handler = async (event: RoutePayload) => {
|
||||
@@ -138,17 +137,17 @@ const handler = async (event: RoutePayload) => {
|
||||
```
|
||||
|
||||
<Note>
|
||||
Header names are normalized to lowercase. Access them using lowercase keys (e.g., `event.headers['content-type']`).
|
||||
Header-Namen werden in Kleinbuchstaben normalisiert. Greifen Sie mit Schlüsseln in Kleinbuchstaben darauf zu (z. B. `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
#### Exposing a function as an AI tool or workflow action
|
||||
#### Eine Funktion als KI-Tool oder Workflow-Aktion verfügbar machen
|
||||
|
||||
Logic functions can be exposed on two surfaces, each with its own trigger:
|
||||
Logikfunktionen können auf zwei Oberflächen verfügbar gemacht werden, jeweils mit eigenem Trigger:
|
||||
|
||||
* **`toolTriggerSettings`** — makes the function discoverable by Twenty's AI features (chat, MCP, function calling). Uses standard JSON Schema, the format LLMs natively understand.
|
||||
* **`workflowActionTriggerSettings`** — makes the function appear as a step in the visual workflow builder. Uses Twenty's rich `InputSchema` so the builder can render proper field editors, variable pickers, and labels.
|
||||
* **`toolTriggerSettings`** — macht die Funktion über die KI-Funktionen von Twenty (Chat, MCP, Funktionsaufrufe) auffindbar. Verwendet das standardmäßige JSON Schema, das Format, das LLMs nativ verstehen.
|
||||
* **`workflowActionTriggerSettings`** — lässt die Funktion als Schritt im visuellen Workflow-Builder erscheinen. Verwendet das umfangreiche `InputSchema` von Twenty, sodass der Builder geeignete Feldeditoren, Variablenauswahlen und Beschriftungen rendern kann.
|
||||
|
||||
A function can opt into one, the other, or both. They sit alongside `cronTriggerSettings`, `databaseEventTriggerSettings`, and `httpRouteTriggerSettings` — same pattern, same shape.
|
||||
Eine Funktion kann sich für eine, die andere oder beide entscheiden. Sie stehen neben `cronTriggerSettings`, `databaseEventTriggerSettings` und `httpRouteTriggerSettings` — gleiches Muster, gleiche Struktur.
|
||||
|
||||
```ts src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -182,10 +181,10 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Hauptpunkte:
|
||||
|
||||
* A function can mix surfaces — declare both `toolTriggerSettings` and `workflowActionTriggerSettings` to expose it in chat AND in the workflow builder.
|
||||
* `toolTriggerSettings.inputSchema` and `workflowActionTriggerSettings.inputSchema` are both optional. When omitted, the manifest builder infers them from the handler source code (JSON Schema for the AI tool, Twenty's `InputSchema` for the workflow action). Provide one explicitly when you want richer typing — for example, with `FieldMetadataType`-aware fields like `CURRENCY` or `RELATION` for the workflow builder, or with `description` fields the AI agent can read:
|
||||
* Eine Funktion kann Oberflächen mischen — deklarieren Sie sowohl `toolTriggerSettings` als auch `workflowActionTriggerSettings`, um sie im Chat UND im Workflow-Builder bereitzustellen.
|
||||
* `toolTriggerSettings.inputSchema` und `workflowActionTriggerSettings.inputSchema` sind beide optional. Wenn sie weggelassen werden, leitet der Manifest-Builder sie aus dem Handler-Quellcode ab (JSON Schema für das KI-Tool, das `InputSchema` von Twenty für die Workflow-Aktion). Geben Sie eines explizit an, wenn Sie eine reichere Typisierung wünschen — zum Beispiel mit `FieldMetadataType`-fähigen Feldern wie `CURRENCY` oder `RELATION` für den Workflow-Builder oder mit `description`-Feldern, die der KI-Agent lesen kann:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
@@ -210,29 +209,29 @@ export default defineLogicFunction({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Write a good `description`.** AI agents rely on the function's `description` field to decide when to use the tool. Be specific about what the tool does and when it should be called.
|
||||
**Schreiben Sie eine gute `description`.** KI-Agenten verlassen sich auf das `description`-Feld der Funktion, um zu entscheiden, wann das Tool verwendet werden soll. Seien Sie konkret darin, was das Tool tut und wann es aufgerufen werden soll.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<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](/l/de/developers/extend/apps/config/install-hooks) for `definePreInstallLogicFunction` and `definePostInstallLogicFunction`.
|
||||
**Installations-Hooks** – Vorinstallations- und Nachinstallations-Handler – teilen sich diese Laufzeit, werden aber mit ihren eigenen define-Funktionen deklariert und verwenden keine Trigger-Einstellungen. Siehe [Installations-Hooks](/l/de/developers/extend/apps/config/install-hooks) für `definePreInstallLogicFunction` und `definePostInstallLogicFunction`.
|
||||
</Note>
|
||||
|
||||
## Typed API clients (twenty-client-sdk)
|
||||
## Typisierte API-Clients (twenty-client-sdk)
|
||||
|
||||
The `twenty-client-sdk` package provides two typed GraphQL clients for interacting with the Twenty API from your logic functions and front components.
|
||||
Das Paket `twenty-client-sdk` stellt zwei typisierte GraphQL-Clients bereit, um aus Ihren Logikfunktionen und Frontend-Komponenten mit der Twenty-API zu interagieren.
|
||||
|
||||
| Client | Import | Endpoint | Generated? |
|
||||
| ------------------- | ---------------------------- | ---------------------------------------------- | ---------------------- |
|
||||
| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — workspace data (records, objects) | Yes, at dev/build time |
|
||||
| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — workspace config, file uploads | No, ships pre-built |
|
||||
| Client | Importieren | Endpunkt | Generiert? |
|
||||
| ------------------- | ---------------------------- | --------------------------------------------------------- | ------------------------------------ |
|
||||
| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — Arbeitsbereichsdaten (Datensätze, Objekte) | Ja, zur Entwicklungs-/Build-Zeit |
|
||||
| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — Arbeitsbereichskonfiguration, Datei-Uploads | Nein, wird vorgefertigt ausgeliefert |
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="CoreApiClient" description="Query and mutate workspace data (records, objects)">
|
||||
<Accordion title="CoreApiClient" description="Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern">
|
||||
|
||||
`CoreApiClient` is the main client for querying and mutating workspace data. It is **generated from your workspace schema** during `yarn twenty dev` or `yarn twenty build`, so it is fully typed to match your objects and fields.
|
||||
Der `CoreApiClient` ist der Haupt-Client zum Abfragen und Ändern von Arbeitsbereichsdaten. Er wird während `yarn twenty dev` oder `yarn twenty build` **aus Ihrem Arbeitsbereichsschema generiert** und ist daher vollständig typisiert, passend zu Ihren Objekten und Feldern.
|
||||
|
||||
```ts
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
@@ -269,15 +268,15 @@ const { createCompany } = await client.mutation({
|
||||
});
|
||||
```
|
||||
|
||||
The client uses a selection-set syntax: pass `true` to include a field, use `__args` for arguments, and nest objects for relations. You get full autocompletion and type checking based on your workspace schema.
|
||||
Der Client verwendet eine Selection-Set-Syntax: Übergeben Sie `true`, um ein Feld einzuschließen, verwenden Sie `__args` für Argumente, und verschachteln Sie Objekte für Relationen. Sie erhalten vollständige Autovervollständigung und Typprüfung basierend auf Ihrem Arbeitsbereichsschema.
|
||||
|
||||
<Note>
|
||||
**CoreApiClient is generated at dev/build time.** If you use it without running `yarn twenty dev` or `yarn twenty build` first, it throws an error. The generation happens automatically — the CLI introspects your workspace's GraphQL schema and generates a typed client using `@genql/cli`.
|
||||
**Der CoreApiClient wird zur Entwicklungs-/Build-Zeit generiert.** Wenn Sie ihn verwenden, ohne zuvor `yarn twenty dev` oder `yarn twenty build` ausgeführt zu haben, wird ein Fehler ausgelöst. Die Generierung erfolgt automatisch — die CLI inspiziert das GraphQL-Schema Ihres Arbeitsbereichs und erzeugt mit `@genql/cli` einen typisierten Client.
|
||||
</Note>
|
||||
|
||||
#### Using CoreSchema for type annotations
|
||||
#### Verwendung von CoreSchema für Typannotationen
|
||||
|
||||
`CoreSchema` provides TypeScript types matching your workspace objects — useful for typing component state or function parameters:
|
||||
`CoreSchema` stellt TypeScript-Typen bereit, die Ihren Arbeitsbereichsobjekten entsprechen — nützlich zum Typisieren von Komponentenzustand oder Funktionsparametern:
|
||||
|
||||
```ts
|
||||
import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core';
|
||||
@@ -299,9 +298,9 @@ setCompany(result.company);
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="MetadataApiClient" description="Workspace config, applications, and file uploads">
|
||||
<Accordion title="MetadataApiClient" description="Konfiguration des Arbeitsbereichs, Anwendungen und Dateiuploads">
|
||||
|
||||
`MetadataApiClient` ships pre-built with the SDK (no generation required). It queries the `/metadata` endpoint for workspace configuration, applications, and file uploads.
|
||||
`MetadataApiClient` ist im SDK bereits vorgefertigt enthalten (keine Generierung erforderlich). Er fragt den Endpunkt `/metadata` nach Arbeitsbereichskonfiguration, Anwendungen und Datei-Uploads ab.
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
@@ -328,9 +327,9 @@ const { objects } = await metadataClient.query({
|
||||
});
|
||||
```
|
||||
|
||||
#### Uploading files
|
||||
#### Dateien hochladen
|
||||
|
||||
`MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields:
|
||||
Der `MetadataApiClient` enthält eine Methode `uploadFile`, um Dateien an Felder des Typs Datei anzuhängen:
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
@@ -351,25 +350,25 @@ console.log(uploadedFile);
|
||||
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ---------------------------------- | -------- | --------------------------------------------------------------- |
|
||||
| `fileBuffer` | `Buffer` | The raw file contents |
|
||||
| `filename` | `string` | The name of the file (used for storage and display) |
|
||||
| `contentType` | `string` | MIME type (defaults to `application/octet-stream` if omitted) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | The `universalIdentifier` of the file-type field on your object |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| ---------------------------------- | -------- | --------------------------------------------------------------------- |
|
||||
| `fileBuffer` | `Buffer` | Der Rohinhalt der Datei |
|
||||
| `filename` | `string` | Der Name der Datei (wird für Speicherung und Anzeige verwendet) |
|
||||
| `contentType` | `string` | MIME-Typ (standardmäßig `application/octet-stream`, wenn weggelassen) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | Der `universalIdentifier` des Dateityp-Felds in Ihrem Objekt |
|
||||
|
||||
Key points:
|
||||
* Uses the field's `universalIdentifier` (not its workspace-specific ID), so your upload code works across any workspace where your app is installed.
|
||||
* The returned `url` is a signed URL you can use to access the uploaded file.
|
||||
Hauptpunkte:
|
||||
* Sie verwendet den `universalIdentifier` des Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist.
|
||||
* Die zurückgegebene `url` ist eine signierte URL, mit der Sie auf die hochgeladene Datei zugreifen können.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
When your code runs on Twenty (logic functions or front components), the platform injects credentials as environment variables:
|
||||
Wenn Ihr Code auf Twenty ausgeführt wird (Logikfunktionen oder Frontend-Komponenten), injiziert die Plattform Anmeldedaten als Umgebungsvariablen:
|
||||
|
||||
* `TWENTY_API_URL` — Base URL of the Twenty API
|
||||
* `TWENTY_APP_ACCESS_TOKEN` — Short-lived key scoped to your application's default function role
|
||||
* `TWENTY_API_URL` — Basis-URL der Twenty-API
|
||||
* `TWENTY_APP_ACCESS_TOKEN` — Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist
|
||||
|
||||
You do **not** need to pass these to the clients — they read from `process.env` automatically. The API key's permissions are determined by the role referenced in `defaultRoleUniversalIdentifier` in your `application-config.ts`.
|
||||
Sie müssen diese **nicht** an die Clients übergeben — sie lesen automatisch aus `process.env`. Die Berechtigungen des API-Schlüssels werden durch die Rolle bestimmt, auf die in `defaultRoleUniversalIdentifier` in Ihrer `application-config.ts` verwiesen wird.
|
||||
</Note>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Server-side TypeScript that runs inside Twenty — triggered by HTTP routes, cron schedules, database events, AI tools, or workflow actions.
|
||||
title: Übersicht
|
||||
description: Serverseitiges TypeScript, das innerhalb von Twenty ausgeführt wird – ausgelöst durch HTTP-Routen, Cron-Zeitpläne, Datenbankereignisse, KI-Tools oder Workflow-Aktionen.
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
A Twenty app's **logic layer** is the code that *runs* — server-side TypeScript handlers reacting to HTTP requests, cron schedules, and record changes; AI skills and agents that live inside the workspace; and OAuth connections that let your functions act on a user's behalf in third-party services.
|
||||
Die **Logikschicht** einer Twenty-App ist der Code, der *ausgeführt wird* – serverseitige TypeScript-Handler, die auf HTTP-Anfragen, Cron-Zeitpläne und Datensatzänderungen reagieren; KI-Skills und -Agenten, die innerhalb des Workspaces leben; und OAuth-Verbindungen, die es Ihren Funktionen ermöglichen, im Namen eines Benutzers in Drittanbieterdiensten zu agieren.
|
||||
|
||||
```text
|
||||
┌─ HTTP route ──┐
|
||||
@@ -22,34 +22,34 @@ A Twenty app's **logic layer** is the code that *runs* — server-side TypeScrip
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## In diesem Abschnitt
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Logic Functions" icon="bolt" href="/l/de/developers/extend/apps/logic/logic-functions">
|
||||
The core building block — trigger types, payloads, and the typed API client.
|
||||
<Card title="Logikfunktionen" icon="bolt" href="/l/de/developers/extend/apps/logic/logic-functions">
|
||||
Der zentrale Baustein – Auslösertypen, Payloads und der typisierte API-Client.
|
||||
</Card>
|
||||
<Card title="Skills & Agents" icon="robot" href="/l/de/developers/extend/apps/logic/skills-and-agents">
|
||||
Reusable AI agent instructions and assistants with custom system prompts.
|
||||
<Card title="Fähigkeiten & Agenten" icon="robot" href="/l/de/developers/extend/apps/logic/skills-and-agents">
|
||||
Wiederverwendbare KI-Agenten-Anweisungen und Assistenten mit benutzerdefinierten System-Prompts.
|
||||
</Card>
|
||||
<Card title="Connections" icon="plug" href="/l/de/developers/extend/apps/logic/connections">
|
||||
OAuth credentials your app holds for third-party services — Linear, GitHub, Slack, and more.
|
||||
<Card title="Verbindungen" icon="plug" href="/l/de/developers/extend/apps/logic/connections">
|
||||
OAuth-Anmeldedaten, die Ihre App für Dienste von Drittanbietern hält – Linear, GitHub, Slack und mehr.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Trigger types at a glance
|
||||
## Auslösertypen im Überblick
|
||||
|
||||
A logic function picks one or more triggers — every entry below is a separate field on `defineLogicFunction()`:
|
||||
Eine Logikfunktion wählt einen oder mehrere Auslöser – jeder Eintrag unten ist ein eigenes Feld auf `defineLogicFunction()`:
|
||||
|
||||
| Trigger | When it runs | Setting |
|
||||
| ------------------- | ---------------------------------------------------------- | ------------------------------- |
|
||||
| **HTTP route** | A request hits your `/s/\<path>` endpoint | `httpRouteTriggerSettings` |
|
||||
| **Cron** | A CRON expression matches | `cronTriggerSettings` |
|
||||
| **Database event** | A workspace record is created, updated, or deleted | `databaseEventTriggerSettings` |
|
||||
| **AI tool** | A Twenty AI feature decides to call your function | `toolTriggerSettings` |
|
||||
| **Workflow action** | A workflow step invokes your function | `workflowActionTriggerSettings` |
|
||||
| Auslöser | Wann sie ausgeführt wird | Einstellung |
|
||||
| --------------------- | -------------------------------------------------------------------- | ------------------------------- |
|
||||
| **HTTP-Route** | Eine Anfrage trifft auf Ihren `/s/\<path>`-Endpunkt | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Ein CRON-Ausdruck stimmt überein | `cronTriggerSettings` |
|
||||
| **Datenbankereignis** | Ein Workspace-Datensatz wird erstellt, aktualisiert oder gelöscht | `databaseEventTriggerSettings` |
|
||||
| **KI-Tool** | Eine Twenty-KI-Funktion entscheidet sich, Ihre Funktion aufzurufen | `toolTriggerSettings` |
|
||||
| **Workflow-Aktion** | Ein Workflow-Schritt ruft Ihre Funktion auf | `workflowActionTriggerSettings` |
|
||||
|
||||
Functions run sandboxed in isolated Node.js processes and access the workspace through a typed API client scoped to the role declared on [`defineApplication()`](/l/de/developers/extend/apps/config/application).
|
||||
Funktionen werden in isolierten Node.js-Prozessen sandboxed ausgeführt und greifen über einen typisierten API-Client, der auf die in [`defineApplication()`](/l/de/developers/extend/apps/config/application) deklarierte Rolle beschränkt ist, auf den Workspace zu.
|
||||
|
||||
<Note>
|
||||
**Install-time hooks** — code that runs before or after the install — share this runtime but use their own define functions and live under [Config → Install Hooks](/l/de/developers/extend/apps/config/install-hooks).
|
||||
**Installations-Hooks zur Installationszeit** – Code, der vor oder nach der Installation ausgeführt wird – teilen sich diese Laufzeitumgebung, verwenden jedoch ihre eigenen define-Funktionen und befinden sich unter [Config → Install Hooks](/l/de/developers/extend/apps/config/install-hooks).
|
||||
</Note>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Skills & Agents
|
||||
description: Define AI skills and agents for your app.
|
||||
title: Fähigkeiten & Agenten
|
||||
description: Definieren Sie KI-Skills und Agenten für Ihre App.
|
||||
icon: robot
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Skills and agents are currently in alpha. The feature works but is still evolving.
|
||||
Fähigkeiten und Agenten befinden sich derzeit in der Alpha-Phase. Die Funktion ist funktionsfähig, entwickelt sich jedoch noch weiter.
|
||||
</Warning>
|
||||
|
||||
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
|
||||
Apps können KI-Funktionen definieren, die im Arbeitsbereich verfügbar sind — wiederverwendbare Skill-Anweisungen und Agenten mit benutzerdefinierten System-Prompts.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineSkill" description="Define AI agent skills">
|
||||
<Accordion title="defineSkill" description="Skills für KI-Agenten definieren">
|
||||
|
||||
Skills define reusable instructions and capabilities that AI agents can use within your workspace. Use `defineSkill()` to define skills with built-in validation:
|
||||
Skills definieren wiederverwendbare Anweisungen und Fähigkeiten, die KI-Agenten in Ihrem Arbeitsbereich verwenden können. Verwenden Sie `defineSkill()`, um Skills mit eingebauter Validierung zu definieren:
|
||||
|
||||
```ts src/skills/example-skill.ts
|
||||
import { defineSkill } from 'twenty-sdk/define';
|
||||
@@ -32,17 +32,17 @@ export default defineSkill({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is a unique identifier string for the skill (kebab-case recommended).
|
||||
* `label` is the human-readable display name shown in the UI.
|
||||
* `content` contains the skill instructions — this is the text the AI agent uses.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `description` (optional) provides additional context about the skill's purpose.
|
||||
Hauptpunkte:
|
||||
* `name` ist eine eindeutige Kennung (als Zeichenfolge) für den Skill (kebab-case empfohlen).
|
||||
* `label` ist der menschenlesbare Anzeigename, der in der UI angezeigt wird.
|
||||
* `content` enthält die Skill-Anweisungen — dies ist der Text, den der KI-Agent verwendet.
|
||||
* `icon` (optional) legt das in der UI angezeigte Symbol fest.
|
||||
* `description` (optional) liefert zusätzlichen Kontext zum Zweck des Skills.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="defineAgent" description="Define AI agents with custom prompts">
|
||||
<Accordion title="defineAgent" description="KI-Agenten mit benutzerdefinierten Prompts definieren">
|
||||
|
||||
Agents are AI assistants that live inside your workspace. Use `defineAgent()` to create agents with a custom system prompt:
|
||||
Agenten sind KI-Assistenten, die innerhalb Ihres Arbeitsbereichs leben. Verwenden Sie `defineAgent()`, um Agenten mit einem benutzerdefinierten System-Prompt zu erstellen:
|
||||
|
||||
```ts src/agents/example-agent.ts
|
||||
import { defineAgent } from 'twenty-sdk/define';
|
||||
@@ -57,13 +57,13 @@ export default defineAgent({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is the unique identifier string for the agent (kebab-case recommended).
|
||||
* `label` is the display name shown in the UI.
|
||||
* `prompt` is the system prompt that defines the agent's behavior.
|
||||
* `description` (optional) provides context about what the agent does.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `modelId` (optional) overrides the default AI model used by the agent.
|
||||
Hauptpunkte:
|
||||
* `name` ist eine eindeutige Kennung (als Zeichenfolge) für den Agenten (kebab-case empfohlen).
|
||||
* `label` ist der in der UI angezeigte Anzeigename.
|
||||
* `prompt` ist der System-Prompt, der das Verhalten des Agenten definiert.
|
||||
* `description` (optional) liefert Kontext dazu, was der Agent tut.
|
||||
* `icon` (optional) legt das in der UI angezeigte Symbol fest.
|
||||
* `modelId` (optional) überschreibt das vom Agenten verwendete Standard-KI-Modell.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: CLI
|
||||
description: yarn twenty commands for executing functions, streaming logs, managing app installations, and switching remotes.
|
||||
icon: terminal
|
||||
description: yarn twenty Befehle zum Ausführen von Funktionen, Streamen von Logs, Verwalten von App-Installationen und Wechseln von Remotes.
|
||||
icon: Terminal
|
||||
---
|
||||
|
||||
Beyond `dev`, `build`, `add`, and `typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
|
||||
Zusätzlich zu `dev`, `build`, `add` und `typecheck` bietet die `yarn twenty` CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen.
|
||||
|
||||
## Executing functions (`yarn twenty exec`)
|
||||
## Funktionen ausführen (`yarn twenty exec`)
|
||||
|
||||
Run a logic function manually without triggering it via HTTP, cron, or database event:
|
||||
Eine Logikfunktion manuell ausführen, ohne sie über HTTP, Cron oder ein Datenbankereignis auszulösen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Execute by function name
|
||||
@@ -24,9 +24,9 @@ yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
## Viewing function logs (`yarn twenty logs`)
|
||||
## Funktionsprotokolle ansehen (`yarn twenty logs`)
|
||||
|
||||
Stream execution logs for your app's logic functions:
|
||||
Ausführungsprotokolle für die Logikfunktionen Ihrer App streamen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Stream all function logs
|
||||
@@ -40,12 +40,12 @@ yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is different from `yarn twenty server logs`, which shows the Docker container logs. `yarn twenty logs` shows your app's function execution logs from the Twenty server.
|
||||
Dies unterscheidet sich von `yarn twenty server logs`, das die Docker-Container-Logs anzeigt. `yarn twenty logs` zeigt die Funktionsausführungsprotokolle Ihrer App vom Twenty-Server.
|
||||
</Note>
|
||||
|
||||
## Uninstalling an app (`yarn twenty uninstall`)
|
||||
## Eine App deinstallieren (`yarn twenty uninstall`)
|
||||
|
||||
Remove your app from the active workspace:
|
||||
Entfernen Sie Ihre App aus dem aktiven Arbeitsbereich:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty uninstall
|
||||
@@ -54,9 +54,9 @@ yarn twenty uninstall
|
||||
yarn twenty uninstall --yes
|
||||
```
|
||||
|
||||
## Managing remotes
|
||||
## Remotes verwalten
|
||||
|
||||
A **remote** is a Twenty server that your app connects to. During setup, the scaffolder creates one for you automatically. You can add more remotes or switch between them at any time.
|
||||
Ein **Remote** ist ein Twenty-Server, mit dem sich Ihre App verbindet. Während der Einrichtung erstellt das Scaffolding-Tool automatisch eines für Sie. Sie können jederzeit weitere Remotes hinzufügen oder zwischen ihnen wechseln.
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new remote (opens a browser for OAuth login)
|
||||
@@ -75,4 +75,4 @@ yarn twenty remote list
|
||||
yarn twenty remote switch <name>
|
||||
```
|
||||
|
||||
Your credentials are stored in `~/.twenty/config.json`.
|
||||
Ihre Anmeldedaten werden in `~/.twenty/config.json` gespeichert.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Build, test, and ship your app — CLI commands, integration tests, CI, and publishing to a server or to npm.
|
||||
title: Übersicht
|
||||
description: Erstellen, testen und ausliefern Sie Ihre App – CLI-Befehle, Integrationstests, CI und das Veröffentlichen auf einem Server oder auf npm.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
The **operations layer** is everything you do *to* your app rather than *with* it: invoking CLI commands, running integration tests against a real Twenty server, configuring CI, and shipping releases — either as a tarball deployed to a single server or as an npm package listed in the marketplace.
|
||||
Die **Operationsschicht** umfasst alles, was Sie *mit* Ihrer App tun, anstatt *in* ihr: das Ausführen von CLI-Befehlen, das Starten von Integrationstests gegen einen realen Twenty-Server, das Konfigurieren von CI und das Ausliefern von Releases – entweder als Tarball, der auf einem einzelnen Server bereitgestellt wird, oder als npm-Paket, das im Marketplace gelistet ist.
|
||||
|
||||
```text
|
||||
develop ─▶ test ─▶ build ─▶ deploy / publish
|
||||
@@ -14,16 +14,16 @@ The **operations layer** is everything you do *to* your app rather than *with* i
|
||||
dev build yarn twenty publish (npm → marketplace)
|
||||
```
|
||||
|
||||
## In this section
|
||||
## In diesem Abschnitt
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="CLI" icon="terminal" href="/l/de/developers/extend/apps/operations/cli">
|
||||
`yarn twenty` reference — exec, logs, uninstall, remotes.
|
||||
<Card title="CLI" icon="Terminal" href="/l/de/developers/extend/apps/operations/cli">
|
||||
`yarn twenty`-Referenz – exec, logs, uninstall, remotes.
|
||||
</Card>
|
||||
<Card title="Testing" icon="flask" href="/l/de/developers/extend/apps/operations/testing">
|
||||
Vitest setup, integration tests, type checking, CI workflow.
|
||||
<Card title="Tests" icon="flask" href="/l/de/developers/extend/apps/operations/testing">
|
||||
Vitest-Setup, Integrationstests, Typprüfung, CI-Workflow.
|
||||
</Card>
|
||||
<Card title="Publishing" icon="upload" href="/l/de/developers/extend/apps/operations/publishing">
|
||||
Build, deploy a tarball, publish to npm, install.
|
||||
<Card title="Veröffentlichen" icon="hochladen" href="/l/de/developers/extend/apps/operations/publishing">
|
||||
Build, Tarball bereitstellen, auf npm veröffentlichen, installieren.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
---
|
||||
title: Publishing
|
||||
icon: upload
|
||||
description: Distribute your Twenty app to the marketplace or deploy it internally.
|
||||
title: Veröffentlichen
|
||||
icon: hochladen
|
||||
description: Veröffentlichen Sie Ihre Twenty-App auf dem Twenty-Marktplatz oder stellen Sie sie intern bereit.
|
||||
---
|
||||
|
||||
## Overview
|
||||
## Übersicht
|
||||
|
||||
Once your app is [built and tested locally](/l/de/developers/extend/apps/getting-started/concepts), you have two paths for distributing it:
|
||||
Sobald Ihre App [lokal gebaut und getestet](/l/de/developers/extend/apps/getting-started/concepts) wurde, haben Sie zwei Möglichkeiten, sie zu verteilen:
|
||||
|
||||
* **Deploy a tarball** — upload your app directly to a specific Twenty server for internal or private use.
|
||||
* **Publish to npm** — list your app in the Twenty marketplace for any workspace to discover and install.
|
||||
* **Einen Tarball bereitstellen** — Laden Sie Ihre App direkt auf einen bestimmten Twenty-Server für die interne oder private Nutzung hoch.
|
||||
* **Auf npm veröffentlichen** — führen Sie Ihre App im Twenty-Marktplatz auf, damit jeder Arbeitsbereich sie entdecken und installieren kann.
|
||||
|
||||
Both paths start from the same **build** step.
|
||||
Beide Pfade beginnen mit demselben **Build**-Schritt.
|
||||
|
||||
## Building your app
|
||||
## Erstellen Ihrer App
|
||||
|
||||
Run the build command to compile your app and generate a distribution-ready `manifest.json`:
|
||||
Führen Sie den Build-Befehl aus, um Ihre App zu kompilieren und eine distributionsfertige `manifest.json` zu erzeugen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty build
|
||||
```
|
||||
|
||||
This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the deploy command.
|
||||
Dabei werden TypeScript-Quelltexte kompiliert, Logikfunktionen und Frontend-Komponenten transpiliert und alles in `.twenty/output/` geschrieben. Fügen Sie `--tarball` hinzu, um zusätzlich ein `.tgz`-Paket für die manuelle Verteilung oder den Deploy-Befehl zu erzeugen.
|
||||
|
||||
## Deploying to a server (tarball)
|
||||
## Bereitstellung auf einem Server (Tarball)
|
||||
|
||||
For apps you don't want publicly available — proprietary tools, enterprise-only integrations, or experimental builds — you can deploy a tarball directly to a Twenty server.
|
||||
Für Apps, die Sie nicht öffentlich verfügbar machen möchten — proprietäre Tools, ausschließlich für Unternehmen bestimmte Integrationen oder experimentelle Builds — können Sie einen Tarball direkt auf einem Twenty-Server bereitstellen.
|
||||
|
||||
### Prerequisites
|
||||
### Voraussetzungen
|
||||
|
||||
Before deploying, you need a configured remote pointing to the target server. Remotes store the server URL and authentication credentials locally in `~/.twenty/config.json`.
|
||||
Bevor Sie bereitstellen, benötigen Sie ein konfiguriertes Remote, das auf den Zielserver zeigt. Remotes speichern die Server-URL und Anmeldeinformationen lokal in `~/.twenty/config.json`.
|
||||
|
||||
Add a remote:
|
||||
Ein Remote hinzufügen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty remote add --api-url https://your-twenty-server.com --as production
|
||||
```
|
||||
|
||||
### Deploying
|
||||
### Bereitstellen
|
||||
|
||||
Build and upload your app to the server in one step:
|
||||
Bauen und laden Sie Ihre App in einem Schritt auf den Server hoch:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty deploy
|
||||
@@ -47,39 +47,39 @@ yarn twenty deploy
|
||||
# yarn twenty deploy --remote production
|
||||
```
|
||||
|
||||
### Sharing a deployed app
|
||||
### Eine bereitgestellte App freigeben
|
||||
|
||||
<Warning>
|
||||
Sharing private (tarball) apps across workspaces is an **Enterprise** feature. The **Distribution** tab will show an upgrade prompt instead of the share controls until your workspace has a valid Enterprise key. See [Settings > Admin Panel > Enterprise](/settings/admin-panel#enterprise) to activate it.
|
||||
Das Teilen privater (Tarball-)Apps über Arbeitsbereiche hinweg ist eine **Enterprise**-Funktion. Die Registerkarte **Distribution** zeigt anstelle der Freigabeoptionen eine Aufforderung zum Upgrade an, bis Ihr Arbeitsbereich über einen gültigen Enterprise-Schlüssel verfügt. Gehen Sie zu [Einstellungen > Admin-Panel > Enterprise](/settings/admin-panel#enterprise), um es zu aktivieren.
|
||||
</Warning>
|
||||
|
||||
Tarball apps are not listed in the public marketplace, so other workspaces on the same server won't discover them by browsing. Once your workspace is on the Enterprise plan, you can share a deployed app like this:
|
||||
Tarball-Apps werden nicht im öffentlichen Marktplatz gelistet, daher entdecken andere Arbeitsbereiche auf demselben Server sie nicht durch Stöbern. Sobald sich Ihr Arbeitsbereich im Enterprise-Plan befindet, können Sie eine bereitgestellte App wie folgt freigeben:
|
||||
|
||||
1. Go to **Settings > Applications > Registrations** and open your app
|
||||
2. In the **Distribution** tab, click **Copy share link**
|
||||
3. Share this link with users on other workspaces — it takes them directly to the app's install page
|
||||
1. Gehen Sie zu **Einstellungen > Anwendungen > Registrierungen** und öffnen Sie Ihre App
|
||||
2. Klicken Sie in der Registerkarte **Distribution** auf **Freigabelink kopieren**
|
||||
3. Teilen Sie diesen Link mit Nutzern in anderen Arbeitsbereichen — er führt sie direkt zur Installationsseite der App
|
||||
|
||||
The share link uses the server's base URL (without any workspace subdomain) so it works for any workspace on the server.
|
||||
Der Freigabelink verwendet die Basis-URL des Servers (ohne Workspace-Subdomain), sodass er für jeden Arbeitsbereich auf dem Server funktioniert.
|
||||
|
||||
### Version management
|
||||
### Versionsverwaltung
|
||||
|
||||
When updating an already deployed tarball app, the server requires the `version` in `package.json` to be **strictly higher** (per [semver](https://semver.org) ordering) than the currently deployed version. Re-deploying the same version, or pushing a lower one, is rejected before the tarball is stored — you'll see a `VERSION_ALREADY_EXISTS` error from the CLI.
|
||||
Beim Aktualisieren einer bereits bereitgestellten Tarball-App verlangt der Server, dass die `version` in `package.json` **strikt höher** (gemäß der [semver](https://semver.org)-Reihenfolge) ist als die derzeit bereitgestellte Version. Das erneute Bereitstellen derselben Version oder das Pushen einer niedrigeren Version wird abgelehnt, bevor das Tarball gespeichert wird — in der CLI wird ein `VERSION_ALREADY_EXISTS`-Fehler angezeigt.
|
||||
|
||||
To release an update:
|
||||
So veröffentlichen Sie ein Update:
|
||||
|
||||
1. Bump the `version` field in your `package.json` (e.g. `1.2.3` → `1.2.4`, `1.3.0`, or `2.0.0`)
|
||||
2. Run `yarn twenty deploy` (or `yarn twenty deploy --remote production`)
|
||||
3. Workspaces that have the app installed will see the upgrade available in their settings
|
||||
1. Erhöhen Sie das Feld `version` in Ihrer `package.json` (z. B. `1.2.3` → `1.2.4`, `1.3.0` oder `2.0.0`).
|
||||
2. Führen Sie `yarn twenty deploy` aus (oder `yarn twenty deploy --remote production`)
|
||||
3. Arbeitsbereiche, die die App installiert haben, sehen in ihren Einstellungen, dass ein Upgrade verfügbar ist.
|
||||
|
||||
<Note>
|
||||
Pre-release tags work as expected: bumping `1.0.0-rc.1` → `1.0.0-rc.2` is allowed, and a final release like `1.0.0` is correctly recognized as higher than `1.0.0-rc.5`. The version in `package.json` must itself be a valid semver string.
|
||||
Pre-Release-Tags funktionieren wie erwartet: Das Erhöhen von `1.0.0-rc.1` → `1.0.0-rc.2` ist zulässig, und eine finale Version wie `1.0.0` wird korrekt als höher als `1.0.0-rc.5` erkannt. Die Version in `package.json` muss selbst eine gültige semver-Zeichenfolge sein.
|
||||
</Note>
|
||||
|
||||
{/* TODO: add screenshot of the Upgrade button */}
|
||||
|
||||
### Server version compatibility
|
||||
### Kompatibilität der Serverversionen
|
||||
|
||||
If your app uses a feature introduced in a specific Twenty server version (for example, OAuth providers added in v2.3.0), you should declare the minimum server version your app requires using the `engines.twenty` field in `package.json`:
|
||||
Wenn Ihre App eine Funktion verwendet, die in einer bestimmten Twenty-Serverversion eingeführt wurde (z. B. OAuth-Anbieter, die in v2.3.0 hinzugefügt wurden), sollten Sie die minimale Serverversion, die Ihre App benötigt, mithilfe des Felds `engines.twenty` in `package.json` angeben:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -92,83 +92,83 @@ If your app uses a feature introduced in a specific Twenty server version (for e
|
||||
}
|
||||
```
|
||||
|
||||
The value is a standard [semver range](https://github.com/npm/node-semver#ranges). Common patterns:
|
||||
Der Wert ist ein standardmäßiger [semver-Bereich](https://github.com/npm/node-semver#ranges). Häufige Muster:
|
||||
|
||||
| Range | Meaning |
|
||||
| ---------------------------------- | ------------------------------------------ |
|
||||
| `>=2.3.0` | Any server from 2.3.0 onward |
|
||||
| `>=2.3.0 \<3.0.0` | 2.3.0 or later, but below the next major |
|
||||
| `^2.3.0` | Same as `>=2.3.0 \<3.0.0` |
|
||||
| Bereich | Bedeutung |
|
||||
| ---------------------------------- | ------------------------------------------------------ |
|
||||
| `>=2.3.0` | Jeder Server ab 2.3.0 |
|
||||
| `>=2.3.0 \<3.0.0` | 2.3.0 oder höher, aber unter der nächsten Hauptversion |
|
||||
| `^2.3.0` | Entspricht `>=2.3.0 \<3.0.0` |
|
||||
|
||||
**What happens at deploy and install time:**
|
||||
**Was bei Bereitstellung und Installation passiert:**
|
||||
|
||||
* If `engines.twenty` is set and the target server's version does not satisfy the range, the deploy (tarball upload) or install is rejected with a `SERVER_VERSION_INCOMPATIBLE` error and a message indicating both the required range and the actual server version.
|
||||
* If `engines.twenty` is **not set**, the app is accepted on any server version (backward-compatible with existing apps).
|
||||
* If the server has no `APP_VERSION` configured, the check is skipped.
|
||||
* Wenn `engines.twenty` gesetzt ist und die Version des Zielservers den Bereich nicht erfüllt, wird die Bereitstellung (Tarball-Upload) oder Installation mit dem Fehler `SERVER_VERSION_INCOMPATIBLE` abgelehnt, zusammen mit einer Meldung, die sowohl den erforderlichen Bereich als auch die tatsächliche Serverversion angibt.
|
||||
* Wenn `engines.twenty` nicht gesetzt ist, wird die App auf jeder Serverversion akzeptiert (abwärtskompatibel mit bestehenden Apps).
|
||||
* Wenn auf dem Server keine `APP_VERSION` konfiguriert ist, wird die Prüfung übersprungen.
|
||||
|
||||
<Note>
|
||||
The server is the authoritative check — it validates `engines.twenty` on both tarball upload and workspace install. If you deploy a tarball out-of-band or install from the marketplace, the server still enforces compatibility.
|
||||
Der Server ist die maßgebliche Prüfinstanz — er validiert `engines.twenty` sowohl beim Tarball-Upload als auch bei der Workspace-Installation. Auch wenn Sie einen Tarball außerhalb des regulären Prozesses bereitstellen oder aus dem Marktplatz installieren, erzwingt der Server weiterhin die Kompatibilität.
|
||||
</Note>
|
||||
|
||||
## Automated CI/CD (scaffolded workflows)
|
||||
## Automatisiertes CI/CD (vorgefertigte Workflows)
|
||||
|
||||
Apps generated with `create-twenty-app` ship with two GitHub Actions workflows out of the box, under `.github/workflows/`. They are ready to run as soon as you push the repo to GitHub — no extra setup is needed for CI, and CD only requires a single secret.
|
||||
Apps, die mit `create-twenty-app` erzeugt wurden, enthalten von Haus aus zwei GitHub-Actions-Workflows unter `.github/workflows/`. Sie sind einsatzbereit, sobald Sie das Repository zu GitHub pushen — für CI ist keine zusätzliche Einrichtung erforderlich, und für CD ist nur ein einziges Secret nötig.
|
||||
|
||||
### CI — `ci.yml`
|
||||
|
||||
Runs integration tests on every push to `main` and every pull request.
|
||||
Führt Ihre Integrationstests bei jedem Push auf `main` und bei Pull Requests aus.
|
||||
|
||||
**What it does:**
|
||||
**Was sie macht:**
|
||||
|
||||
1. Checks out your app's source.
|
||||
2. Spawns an isolated Twenty test instance using the `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` composite action (the CI equivalent of `yarn twenty server start --test`).
|
||||
3. Enables Corepack, sets up Node.js from your `.nvmrc`, and installs dependencies with `yarn install --immutable`.
|
||||
4. Runs `yarn test`, passing `TWENTY_API_URL` and `TWENTY_API_KEY` from the spawned instance so your tests can talk to a real server.
|
||||
1. Checkt den Quellcode Ihrer App aus.
|
||||
2. Startet eine isolierte Twenty-Testinstanz mithilfe der Composite-Action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (das CI-Äquivalent zu `yarn twenty server start --test`).
|
||||
3. Aktiviert Corepack, richtet Node.js anhand Ihrer `.nvmrc` ein und installiert Abhängigkeiten mit `yarn install --immutable`.
|
||||
4. Führt `yarn test` aus und übergibt `TWENTY_API_URL` und `TWENTY_API_KEY` aus der gestarteten Instanz, damit Ihre Tests mit einem echten Server kommunizieren können.
|
||||
|
||||
**Config knobs:**
|
||||
**Konfigurationsoptionen:**
|
||||
|
||||
* `TWENTY_VERSION` (env, defaults to `latest`) — pin the Twenty server version used in CI by editing this in `ci.yml`.
|
||||
* Concurrency is grouped by `github.ref` and cancels in-progress runs on new pushes.
|
||||
* `TWENTY_VERSION` (env, standardmäßig `latest`) — fixieren Sie die in CI verwendete Twenty-Server-Version, indem Sie dies in `ci.yml` anpassen.
|
||||
* Die Parallelität wird nach `github.ref` gruppiert und bricht laufende Ausführungen bei neuen Pushes ab.
|
||||
|
||||
No secrets are required — the test instance is ephemeral and lives only for the duration of the job.
|
||||
Es sind keine Secrets erforderlich — die Testinstanz ist flüchtig und existiert nur für die Dauer des Jobs.
|
||||
|
||||
### CD — `cd.yml`
|
||||
|
||||
Deploys your app to a configured Twenty server on every push to `main`, and optionally from a pull request when the `deploy` label is applied.
|
||||
Stellt Ihre App bei jedem Push auf `main` auf einem konfigurierten Twenty-Server bereit und optional aus einem Pull Request, wenn das Label `deploy` gesetzt ist.
|
||||
|
||||
**What it does:**
|
||||
**Was sie macht:**
|
||||
|
||||
1. Checks out the PR head (for labeled PRs) or the pushed commit.
|
||||
2. Runs `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — the CI equivalent of `yarn twenty deploy`.
|
||||
3. Runs `twentyhq/twenty/.github/actions/install-twenty-app@main` so the newly deployed version is installed into the target workspace.
|
||||
1. Checkt den PR-Head (bei PRs mit Label) oder den gepushten Commit aus.
|
||||
2. Führt `twentyhq/twenty/.github/actions/deploy-twenty-app@main` aus — das CI-Äquivalent zu `yarn twenty deploy`.
|
||||
3. Führt `twentyhq/twenty/.github/actions/install-twenty-app@main` aus, damit die neu bereitgestellte Version in den Ziel-Workspace installiert wird.
|
||||
|
||||
**Required configuration:**
|
||||
**Erforderliche Konfiguration:**
|
||||
|
||||
| Setting | Where | Purpose |
|
||||
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `TWENTY_DEPLOY_URL` | `env` in `cd.yml` (defaults to `http://localhost:3000`) | The Twenty server to deploy to. Change this to your real server URL before first use. |
|
||||
| `TWENTY_DEPLOY_API_KEY` | GitHub repo **Settings → Secrets and variables → Actions** | API key with deploy permission on the target server. |
|
||||
| Einstellung | Wo | Zweck |
|
||||
| ----------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TWENTY_DEPLOY_URL` | `env` in `cd.yml` (standardmäßig `http://localhost:3000`) | Der Twenty-Server, auf den bereitgestellt werden soll. Ändern Sie dies vor der ersten Verwendung auf die echte Server-URL. |
|
||||
| `TWENTY_DEPLOY_API_KEY` | GitHub-Repository **Settings → Secrets and variables → Actions** | API-Schlüssel mit Berechtigung zum Bereitstellen auf dem Zielserver. |
|
||||
|
||||
<Note>
|
||||
The default `TWENTY_DEPLOY_URL` of `http://localhost:3000` is a placeholder — it will not reach anything from a GitHub-hosted runner. Update it to your server's public URL (or use a self-hosted runner with network access) before enabling CD.
|
||||
Der Standardwert von `TWENTY_DEPLOY_URL` (`http://localhost:3000`) ist ein Platzhalter — von einem GitHub-gehosteten Runner ist er nicht erreichbar. Aktualisieren Sie sie auf die öffentliche URL Ihres Servers (oder verwenden Sie einen selbstgehosteten Runner mit Netzwerkzugriff), bevor Sie CD aktivieren.
|
||||
</Note>
|
||||
|
||||
**Triggering a preview deploy from a PR:**
|
||||
**Eine Vorschau-Bereitstellung aus einem PR auslösen:**
|
||||
|
||||
Add the `deploy` label to a pull request. The `if:` guard in `cd.yml` will run the job for that PR using the PR's head commit, letting you validate a change on the target server before merging.
|
||||
Fügen Sie einem Pull Request das Label `deploy` hinzu. Die `if:`-Bedingung in `cd.yml` führt den Job für diesen PR mit dem Head-Commit des PR aus, sodass Sie eine Änderung auf dem Zielserver vor dem Mergen validieren können.
|
||||
|
||||
### Pinning the reusable actions
|
||||
### Fixieren der wiederverwendbaren Actions
|
||||
|
||||
Both workflows reference reusable actions at `@main`, so action updates in the `twentyhq/twenty` repo are picked up automatically. If you want deterministic builds, replace `@main` with a commit SHA or release tag on each `uses:` line.
|
||||
Beide Workflows verweisen auf wiederverwendbare Actions mit `@main`, sodass Aktualisierungen der Actions im Repository `twentyhq/twenty` automatisch übernommen werden. Wenn Sie deterministische Builds möchten, ersetzen Sie `@main` in jeder `uses:`-Zeile durch eine Commit-SHA oder einen Release-Tag.
|
||||
|
||||
## Publishing to npm
|
||||
## Auf npm veröffentlichen
|
||||
|
||||
Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twenty workspace can browse, install, and upgrade marketplace apps directly from the UI.
|
||||
Die Veröffentlichung auf npm macht Ihre App im Twenty-Marktplatz auffindbar. Jeder Twenty-Arbeitsbereich kann Marktplatz-Apps direkt über die Benutzeroberfläche durchsuchen, installieren und aktualisieren.
|
||||
|
||||
### Requirements
|
||||
### Anforderungen
|
||||
|
||||
* An [npm](https://www.npmjs.com) account
|
||||
* The `twenty-app` keyword in your `package.json` `keywords` array (add it manually — it is not included by default in the `create-twenty-app` template)
|
||||
* Ein [npm](https://www.npmjs.com)-Konto
|
||||
* Das Schlüsselwort `twenty-app` in Ihrem `package.json`-Array `keywords` (manuell hinzufügen — es ist in der `create-twenty-app`-Vorlage standardmäßig nicht enthalten)
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -178,9 +178,9 @@ Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twe
|
||||
}
|
||||
```
|
||||
|
||||
### Marketplace metadata
|
||||
### Marktplatz-Metadaten
|
||||
|
||||
The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logoUrl` and `screenshots` to reference images from the `public/` folder:
|
||||
Die `defineApplication()`-Konfiguration unterstützt optionale Felder, die steuern, wie Ihre App im Marktplatz erscheint. Verwenden Sie `logoUrl` und `screenshots`, um Bilder aus dem Ordner `public/` zu referenzieren:
|
||||
|
||||
```ts src/application-config.ts
|
||||
export default defineApplication({
|
||||
@@ -196,33 +196,33 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
See the [defineApplication accordion](/l/de/developers/extend/apps/config/application#marketplace-metadata) in the Building Apps page for the full list of marketplace fields (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).
|
||||
Siehe das [defineApplication-Akkordeon](/l/de/developers/extend/apps/config/application#marketplace-metadata) auf der Seite Building Apps für die vollständige Liste der Marktplatzfelder (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl` usw.).
|
||||
|
||||
#### Recommended screenshot dimensions
|
||||
#### Empfohlene Abmessungen für Screenshots
|
||||
|
||||
The marketplace renders `screenshots` in a fixed `8:5` container (for example, `1600×1000 px`).
|
||||
Der Marktplatz stellt `screenshots` in einem festen `8:5`-Container dar (zum Beispiel `1600×1000 px`).
|
||||
|
||||
<Note>
|
||||
Screenshots of any aspect ratio are displayed in full and are never cropped, but anything significantly taller or narrower than `8:5` will show empty bands on the sides.
|
||||
Screenshots mit beliebigem Seitenverhältnis werden vollständig angezeigt und niemals beschnitten, aber alles, was deutlich höher oder schmaler als `8:5` ist, zeigt an den Seiten leere Balken.
|
||||
</Note>
|
||||
|
||||
### Publish
|
||||
### Veröffentlichen
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty publish
|
||||
```
|
||||
|
||||
To publish under a specific dist-tag (e.g., `beta` or `next`):
|
||||
Um unter einem bestimmten dist-tag zu veröffentlichen (z. B. `beta` oder `next`):
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty publish --tag beta
|
||||
```
|
||||
|
||||
### How marketplace discovery works
|
||||
### So funktioniert die Marktplatz-Erkennung
|
||||
|
||||
The Twenty server syncs its marketplace catalog from the npm registry **every hour**.
|
||||
Der Twenty-Server synchronisiert seinen Marktplatzkatalog **stündlich** aus der npm-Registry.
|
||||
|
||||
You can trigger the sync immediately instead of waiting:
|
||||
Sie können die Synchronisierung sofort auslösen, anstatt zu warten:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty server catalog-sync
|
||||
@@ -230,15 +230,15 @@ yarn twenty server catalog-sync
|
||||
# yarn twenty server catalog-sync --remote production
|
||||
```
|
||||
|
||||
The metadata shown in the marketplace comes from your `defineApplication()` config — fields like `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, and `termsUrl`.
|
||||
Die im Marktplatz angezeigten Metadaten stammen aus Ihrer `defineApplication()`-Konfiguration — Felder wie `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` und `termsUrl`.
|
||||
|
||||
<Note>
|
||||
If your app does not define an `aboutDescription` in `defineApplication()`, the marketplace will automatically use your package's `README.md` from npm as the about page content. This means you can maintain a single README for both npm and the Twenty marketplace. If you want a different description in the marketplace, explicitly set `aboutDescription`.
|
||||
Wenn Ihre App keine `aboutDescription` in `defineApplication()` definiert, verwendet der Marktplatz automatisch die `README.md` Ihres Pakets von npm als Inhalt der Über-uns-Seite. Das bedeutet, dass Sie eine einzige README sowohl für npm als auch für den Twenty-Marktplatz pflegen können. Wenn Sie im Marktplatz eine andere Beschreibung möchten, setzen Sie `aboutDescription` explizit.
|
||||
</Note>
|
||||
|
||||
### CI publishing
|
||||
### CI-Veröffentlichung
|
||||
|
||||
Use this GitHub Actions workflow to publish automatically on every release (uses [OIDC](https://docs.npmjs.com/trusted-publishers)):
|
||||
Verwenden Sie diesen GitHub-Actions-Workflow, um bei jedem Release automatisch zu veröffentlichen (verwendet [OIDC](https://docs.npmjs.com/trusted-publishers)):
|
||||
|
||||
```yaml filename=".github/workflows/publish.yml"
|
||||
name: Publish
|
||||
@@ -265,31 +265,31 @@ jobs:
|
||||
working-directory: .twenty/output
|
||||
```
|
||||
|
||||
For other CI systems (GitLab CI, CircleCI, etc.), the same three commands apply: `yarn install`, `yarn twenty build`, then `npm publish` from `.twenty/output`.
|
||||
Für andere CI-Systeme (GitLab CI, CircleCI usw.) gelten die gleichen drei Befehle: `yarn install`, `yarn twenty build` und anschließend `npm publish` aus `.twenty/output`.
|
||||
|
||||
<Note>
|
||||
**npm provenance** is optional but recommended. Publishing with `--provenance` adds a trust badge to your npm listing, letting users verify the package was built from a specific commit in a public CI pipeline. See the [npm provenance docs](https://docs.npmjs.com/generating-provenance-statements) for setup instructions.
|
||||
**npm-Provenance** ist optional, wird jedoch empfohlen. Das Veröffentlichen mit `--provenance` fügt Ihrem npm-Eintrag ein Vertrauensabzeichen hinzu, sodass Nutzer überprüfen können, dass das Paket aus einem bestimmten Commit in einer öffentlichen CI-Pipeline gebaut wurde. Siehe die [npm-Provenance-Dokumentation](https://docs.npmjs.com/generating-provenance-statements) für Einrichtungshinweise.
|
||||
</Note>
|
||||
|
||||
## Installing apps
|
||||
## Apps installieren
|
||||
|
||||
Once an app is published (npm) or deployed (tarball), workspaces can install it through the UI.
|
||||
Sobald eine App veröffentlicht (npm) oder bereitgestellt (Tarball) wurde, können Arbeitsbereiche sie über die Benutzeroberfläche installieren.
|
||||
|
||||
Go to the **Settings > Applications** page in Twenty, where both marketplace and tarball-deployed apps can be browsed and installed.
|
||||
Gehen Sie zur Seite **Einstellungen > Anwendungen** in Twenty, auf der sowohl Marktplatz- als auch per Tarball bereitgestellte Apps durchsucht und installiert werden können.
|
||||
|
||||
{/* TODO: add screenshot of the UI when the app is registered */}
|
||||
|
||||
You can also install apps from the command line:
|
||||
Sie können Apps auch über die Befehlszeile installieren:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty install
|
||||
```
|
||||
|
||||
<Note>
|
||||
The server enforces semver versioning on install, mirroring the rules on deploy:
|
||||
Der Server erzwingt bei der Installation semver-Versionierung und spiegelt damit die Regeln beim Bereitstellen wider:
|
||||
|
||||
* Installing the same version that is already installed in your workspace is rejected with an `APP_ALREADY_INSTALLED` error.
|
||||
* Installing a lower version than the one currently installed is rejected with a `CANNOT_DOWNGRADE_APPLICATION` error.
|
||||
* Die Installation derselben Version, die in Ihrem Arbeitsbereich bereits installiert ist, wird mit einem `APP_ALREADY_INSTALLED`-Fehler abgelehnt.
|
||||
* Die Installation einer niedrigeren Version als die aktuell installierte wird mit einem `CANNOT_DOWNGRADE_APPLICATION`-Fehler abgelehnt.
|
||||
|
||||
To install a newer version, deploy or publish it first, then re-run `yarn twenty install`.
|
||||
Um eine neuere Version zu installieren, stellen Sie sie zuerst bereit oder veröffentlichen Sie sie und führen Sie dann `yarn twenty install` erneut aus.
|
||||
</Note>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
---
|
||||
title: Testing
|
||||
description: Vitest setup, integration tests against a real Twenty server, type checking, and CI with GitHub Actions.
|
||||
title: Tests
|
||||
description: Vitest-Setup, Integrationstests gegen einen realen Twenty-Server, Typprüfung und CI mit GitHub Actions.
|
||||
icon: flask
|
||||
---
|
||||
|
||||
The SDK provides programmatic APIs that let you build, deploy, install, and uninstall your app from test code. Combined with [Vitest](https://vitest.dev/) and the typed API clients, you can write integration tests that verify your app works end-to-end against a real Twenty server.
|
||||
Das SDK stellt programmgesteuerte APIs bereit, mit denen Sie Ihre App aus Testcode heraus bauen, bereitstellen, installieren und deinstallieren können. In Kombination mit [Vitest](https://vitest.dev/) und den typisierten API-Clients können Sie Integrationstests schreiben, die prüfen, dass Ihre App End-to-End gegen einen echten Twenty-Server funktioniert.
|
||||
|
||||
## Using npm packages
|
||||
## Verwendung von npm-Paketen
|
||||
|
||||
You can install and use any npm package in your app. Both logic functions and front components are bundled with [esbuild](https://esbuild.github.io/), which inlines all dependencies into the output — no `node_modules` are needed at runtime.
|
||||
Sie können in Ihrer App beliebige npm-Pakete installieren und verwenden. Sowohl Logikfunktionen als auch Frontend-Komponenten werden mit [esbuild](https://esbuild.github.io/) gebündelt, das alle Abhängigkeiten in die Ausgabe einbettet — zur Laufzeit sind keine `node_modules` erforderlich.
|
||||
|
||||
### Installing a package
|
||||
### Ein Paket installieren
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add axios
|
||||
```
|
||||
|
||||
Then import it in your code:
|
||||
Importieren Sie es anschließend in Ihrem Code:
|
||||
|
||||
```ts src/logic-functions/fetch-data.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -37,7 +37,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
The same works for front components:
|
||||
Dasselbe funktioniert für Frontend-Komponenten:
|
||||
|
||||
```tsx src/front-components/chart.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -54,25 +54,25 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
### How bundling works
|
||||
### Wie das Bundling funktioniert
|
||||
|
||||
The build step uses esbuild to produce a single self-contained file per logic function and per front component. All imported packages are inlined into the bundle.
|
||||
Der Build-Schritt verwendet esbuild, um pro Logikfunktion und pro Frontend-Komponente eine einzelne, in sich geschlossene Datei zu erzeugen. Alle importierten Pakete werden in das Bundle eingebettet.
|
||||
|
||||
**Logic functions** run in a Node.js environment. Node built-in modules (`fs`, `path`, `crypto`, `http`, etc.) are available and do not need to be installed.
|
||||
**Logikfunktionen** laufen in einer Node.js-Umgebung. Eingebaute Node.js-Module (`fs`, `path`, `crypto`, `http` usw.) stehen zur Verfügung und müssen nicht installiert werden.
|
||||
|
||||
**Front components** run in a Web Worker. Node built-in modules are **not** available — only browser APIs and npm packages that work in a browser environment.
|
||||
**Frontend-Komponenten** laufen in einem Web Worker. Eingebaute Node.js-Module sind **nicht** verfügbar — nur Browser-APIs und npm-Pakete, die in einer Browserumgebung funktionieren.
|
||||
|
||||
Both environments have `twenty-client-sdk/core` and `twenty-client-sdk/metadata` available as pre-provided modules — these are not bundled but resolved at runtime by the server.
|
||||
In beiden Umgebungen stehen `twenty-client-sdk/core` und `twenty-client-sdk/metadata` als vorab bereitgestellte Module zur Verfügung — sie werden nicht gebündelt, sondern zur Laufzeit vom Server aufgelöst.
|
||||
|
||||
## Setup
|
||||
## Einrichtung
|
||||
|
||||
The scaffolded app already includes Vitest. If you set it up manually, install the dependencies:
|
||||
Die erzeugte App enthält bereits Vitest. Wenn Sie es manuell einrichten, installieren Sie die Abhängigkeiten:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D vitest vite-tsconfig-paths
|
||||
```
|
||||
|
||||
Create a `vitest.config.ts` at the root of your app:
|
||||
Erstellen Sie eine `vitest.config.ts` im Stammverzeichnis Ihrer App:
|
||||
|
||||
```ts vitest.config.ts
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
@@ -98,7 +98,7 @@ export default defineConfig({
|
||||
});
|
||||
```
|
||||
|
||||
Create a setup file that verifies the server is reachable before tests run:
|
||||
Erstellen Sie eine Setup-Datei, die vor dem Testlauf überprüft, dass der Server erreichbar ist:
|
||||
|
||||
```ts src/__tests__/setup-test.ts
|
||||
import * as fs from 'fs';
|
||||
@@ -138,22 +138,22 @@ beforeAll(async () => {
|
||||
});
|
||||
```
|
||||
|
||||
## Programmatic SDK APIs
|
||||
## Programmgesteuerte SDK-APIs
|
||||
|
||||
The `twenty-sdk/cli` subpath exports functions you can call directly from test code:
|
||||
Der Subpfad `twenty-sdk/cli` exportiert Funktionen, die Sie direkt aus Testcode aufrufen können:
|
||||
|
||||
| Function | Description |
|
||||
| -------------- | ------------------------------------------- |
|
||||
| `appBuild` | Build the app and optionally pack a tarball |
|
||||
| `appDeploy` | Upload a tarball to the server |
|
||||
| `appInstall` | Install the app on the active workspace |
|
||||
| `appUninstall` | Uninstall the app from the active workspace |
|
||||
| Funktion | Beschreibung |
|
||||
| -------------- | ----------------------------------------------------- |
|
||||
| `appBuild` | Die App bauen und optional ein Tarball packen |
|
||||
| `appDeploy` | Ein Tarball auf den Server hochladen |
|
||||
| `appInstall` | Die App im aktiven Arbeitsbereich installieren |
|
||||
| `appUninstall` | Die App aus dem aktiven Arbeitsbereich deinstallieren |
|
||||
|
||||
Each function returns a result object with `success: boolean` and either `data` or `error`.
|
||||
Jede Funktion gibt ein Ergebnisobjekt mit `success: boolean` und entweder `data` oder `error` zurück.
|
||||
|
||||
## Writing an integration test
|
||||
## Einen Integrationstest schreiben
|
||||
|
||||
Here is a full example that builds, deploys, and installs the app, then verifies it appears in the workspace:
|
||||
Hier ist ein vollständiges Beispiel, das die App baut, bereitstellt und installiert und anschließend prüft, dass sie im Arbeitsbereich erscheint:
|
||||
|
||||
```ts src/__tests__/app-install.integration-test.ts
|
||||
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
|
||||
@@ -216,40 +216,40 @@ describe('App installation', () => {
|
||||
});
|
||||
```
|
||||
|
||||
## Running tests
|
||||
## Tests ausführen
|
||||
|
||||
Make sure your local Twenty server is running, then:
|
||||
Stellen Sie sicher, dass Ihr lokaler Twenty-Server läuft, und führen Sie dann Folgendes aus:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test
|
||||
```
|
||||
|
||||
Or in watch mode during development:
|
||||
Oder im Watch-Modus während der Entwicklung:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test:watch
|
||||
```
|
||||
|
||||
## Type checking
|
||||
## Typprüfung
|
||||
|
||||
You can also run type checking on your app without running tests:
|
||||
Sie können die Typprüfung Ihrer App auch ohne Tests ausführen:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty typecheck
|
||||
```
|
||||
|
||||
This runs `tsc --noEmit` and reports any type errors.
|
||||
Dies führt `tsc --noEmit` aus und meldet etwaige Typfehler.
|
||||
|
||||
## CI with GitHub Actions
|
||||
## CI mit GitHub Actions
|
||||
|
||||
The scaffolder generates a ready-to-use GitHub Actions workflow at `.github/workflows/ci.yml`. It runs your integration tests automatically on every push to `main` and on pull requests.
|
||||
Das Scaffolding-Tool erzeugt einen einsatzbereiten GitHub-Actions-Workflow in `.github/workflows/ci.yml`. Er führt Ihre Integrationstests automatisch bei jedem Push auf `main` und bei Pull Requests aus.
|
||||
|
||||
The workflow:
|
||||
Der Workflow:
|
||||
|
||||
1. Checks out your code
|
||||
2. Spins up a temporary Twenty server using the `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` action
|
||||
3. Installs dependencies with `yarn install --immutable`
|
||||
4. Runs `yarn test` with `TWENTY_API_URL` and `TWENTY_API_KEY` injected from the action outputs
|
||||
1. Checkt Ihren Code aus
|
||||
2. Startet einen temporären Twenty-Server mit der Aktion `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
|
||||
3. Installiert Abhängigkeiten mit `yarn install --immutable`
|
||||
4. Führt `yarn test` aus, wobei `TWENTY_API_URL` und `TWENTY_API_KEY` aus den Aktionsausgaben injiziert werden.
|
||||
|
||||
```yaml .github/workflows/ci.yml
|
||||
name: CI
|
||||
@@ -296,6 +296,6 @@ jobs:
|
||||
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
|
||||
```
|
||||
|
||||
You don't need to configure any secrets — the `spawn-twenty-docker-image` action starts an ephemeral Twenty server directly in the runner and outputs the connection details. The `GITHUB_TOKEN` secret is provided automatically by GitHub.
|
||||
Sie müssen keine Secrets konfigurieren — die Aktion `spawn-twenty-docker-image` startet einen flüchtigen Twenty-Server direkt im Runner und gibt die Verbindungsdetails aus. Das Secret `GITHUB_TOKEN` wird automatisch von GitHub bereitgestellt.
|
||||
|
||||
To pin a specific Twenty version instead of `latest`, change the `TWENTY_VERSION` environment variable at the top of the workflow.
|
||||
Um eine bestimmte Twenty-Version statt `latest` festzulegen, ändern Sie die Umgebungsvariable `TWENTY_VERSION` oben im Workflow.
|
||||
|
||||
@@ -155,7 +155,27 @@
|
||||
"label": "Übersicht"
|
||||
},
|
||||
"apps": {
|
||||
"label": "Apps"
|
||||
"label": "Apps",
|
||||
"groups": {
|
||||
"appsGettingStarted": {
|
||||
"label": "Erste Schritte"
|
||||
},
|
||||
"appsConfig": {
|
||||
"label": "Konfiguration"
|
||||
},
|
||||
"appsData": {
|
||||
"label": "Daten"
|
||||
},
|
||||
"appsLogic": {
|
||||
"label": "Logik"
|
||||
},
|
||||
"appsLayout": {
|
||||
"label": "Layout"
|
||||
},
|
||||
"appsOperations": {
|
||||
"label": "Operationen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"label": "API"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Application Config
|
||||
description: Declare your app's identity, default role, variables, and marketplace metadata with defineApplication.
|
||||
title: Configuração da aplicação
|
||||
description: Declare a identidade do seu app, o papel padrão, as variáveis e os metadados de marketplace com `defineApplication`.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
Every app must have exactly one `defineApplication` call. It declares:
|
||||
Todo app deve ter exatamente uma chamada a `defineApplication`. Ela declara:
|
||||
|
||||
* **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](/l/pt/developers/extend/apps/logic/logic-functions).
|
||||
* **Identidade** — identificador universal, nome de exibição, descrição.
|
||||
* **Permissões** — qual papel é usado pelas suas funções de lógica e pelos componentes de front-end.
|
||||
* **Variáveis** *(opcional)* — pares chave–valor expostos ao seu código como variáveis de ambiente.
|
||||
* **Hooks de pré-instalação/pós-instalação** *(opcional)* — consulte [Funções de lógica](/l/pt/developers/extend/apps/logic/logic-functions).
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
@@ -31,35 +31,35 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
Notas:
|
||||
|
||||
* `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 (e.g., `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` must reference a role defined with [`defineRole()`](/l/pt/developers/extend/apps/config/roles).
|
||||
* Pre-install and post-install functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
* Os campos `universalIdentifier` são IDs determinísticos que você controla. Gere-os uma vez e mantenha-os estáveis entre sincronizações.
|
||||
* `applicationVariables` tornam-se variáveis de ambiente para suas funções e componentes de front-end (por exemplo, `DEFAULT_RECIPIENT_NAME` fica disponível como `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` deve fazer referência a um papel definido com [`defineRole()`](/l/pt/developers/extend/apps/config/roles).
|
||||
* As funções de pré-instalação e pós-instalação são detectadas automaticamente durante a construção do manifesto — você não precisa referenciá-las em `defineApplication()`.
|
||||
|
||||
## Default function role
|
||||
## Papel de função padrão
|
||||
|
||||
The `defaultRoleUniversalIdentifier` controls what the app's logic functions and front components can access:
|
||||
O `defaultRoleUniversalIdentifier` controla ao que as funções de lógica e os componentes de front-end do app podem acessar:
|
||||
|
||||
* The runtime token injected as `TWENTY_APP_ACCESS_TOKEN` is derived from this role.
|
||||
* The typed API client is restricted to the permissions granted to that role.
|
||||
* Follow least-privilege: declare only the permissions your functions need.
|
||||
* O token em tempo de execução injetado como `TWENTY_APP_ACCESS_TOKEN` é derivado desse papel.
|
||||
* O cliente de API tipado é restrito às permissões concedidas a esse papel.
|
||||
* Siga o princípio do menor privilégio: declare apenas as permissões de que suas funções precisam.
|
||||
|
||||
When you scaffold a new app, the CLI creates a starter role file at `src/roles/default-role.ts`. See [Roles & Permissions](/l/pt/developers/extend/apps/config/roles) for the full reference.
|
||||
Ao criar um novo app com o scaffold, a CLI cria um arquivo de papel inicial em `src/roles/default-role.ts`. Consulte [Papéis e permissões](/l/pt/developers/extend/apps/config/roles) para a referência completa.
|
||||
|
||||
## Marketplace metadata
|
||||
## Metadados do Marketplace
|
||||
|
||||
If you plan to [publish your app](/l/pt/developers/extend/apps/operations/publishing), these optional fields control how it appears in the marketplace:
|
||||
Se você planeja [publicar seu app](/l/pt/developers/extend/apps/operations/publishing), estes campos opcionais controlam como seu app aparece no marketplace:
|
||||
|
||||
| Field | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Author or company name |
|
||||
| `category` | App category for marketplace filtering |
|
||||
| `logoUrl` | Path to your app logo (e.g., `public/logo.png`) |
|
||||
| `screenshots` | Array of screenshot paths (e.g., `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm |
|
||||
| `websiteUrl` | Link to your website |
|
||||
| `termsUrl` | Link to terms of service |
|
||||
| `emailSupport` | Support email address |
|
||||
| `issueReportUrl` | Link to issue tracker |
|
||||
| Campo | Descrição |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Nome do autor ou da empresa |
|
||||
| `category` | Categoria do app para filtragem no marketplace |
|
||||
| `logoUrl` | Caminho para o logo do seu app (por exemplo, `public/logo.png`) |
|
||||
| `screenshots` | Array de caminhos de capturas de tela (por exemplo, `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Descrição em markdown mais longa para a aba "Sobre". Se omitido, o marketplace usa o `README.md` do pacote no npm |
|
||||
| `websiteUrl` | Link para seu site |
|
||||
| `termsUrl` | Link para os Termos de Serviço |
|
||||
| `emailSupport` | Endereço de e-mail de suporte |
|
||||
| `issueReportUrl` | Link para o rastreador de problemas |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Install Hooks
|
||||
description: Run logic before or after the install — seed data, back up records, validate the upgrade.
|
||||
title: Hooks de instalação
|
||||
description: Execute lógica antes ou depois da instalação — para popular dados, fazer backup de registros, validar a atualização.
|
||||
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](/l/pt/developers/extend/apps/logic/logic-functions) and receive an `InstallPayload`, but they're declared with their own define functions — `definePostInstallLogicFunction()` and `definePreInstallLogicFunction()` — and live outside the normal trigger model (HTTP, cron, database events).
|
||||
Hooks de instalação são funções de lógica especiais que são executadas durante o ciclo de vida de instalação ou atualização. Elas compartilham o mesmo runtime de handler que as [logic functions](/l/pt/developers/extend/apps/logic/logic-functions) normais e recebem um `InstallPayload`, mas são declaradas com suas próprias funções de definição — `definePostInstallLogicFunction()` e `definePreInstallLogicFunction()` — e ficam fora do modelo de gatilhos normal (HTTP, cron, eventos de banco de dados).
|
||||
|
||||
Each app may define **at most one pre-install** and **at most one post-install** function. The manifest build will error if more than one of either is detected.
|
||||
Cada aplicativo pode definir **no máximo uma pré-instalação** e **no máximo uma pós-instalação**. A geração do manifesto apresentará erro se mais de uma de cada for detectada.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -20,9 +20,9 @@ Each app may define **at most one pre-install** and **at most one post-install**
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Runs after the workspace metadata migration is applied">
|
||||
<Accordion title="definePostInstallLogicFunction" description="É executada depois que a migração de metadados do workspace é aplicada">
|
||||
|
||||
A post-install function runs automatically once your app has finished installing on a workspace. The server executes it **after** the app's metadata has been synchronized and the SDK client has been generated, so the workspace is fully ready to use and the new schema is in place. Typical use cases include seeding default data, creating initial records, configuring workspace settings, or provisioning resources on third-party services.
|
||||
Uma função de pós-instalação é executada automaticamente assim que seu aplicativo termina de ser instalado em um workspace. O servidor a executa **depois** que os metadados do aplicativo forem sincronizados e o cliente do SDK for gerado, para que o espaço de trabalho esteja totalmente pronto para uso e o novo esquema esteja disponível. Casos de uso típicos incluem popular dados padrão, criar registros iniciais, configurar as definições do espaço de trabalho ou provisionar recursos em serviços de terceiros.
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -42,30 +42,30 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the post-install function at any time using the CLI:
|
||||
Você também pode executar manualmente a função de pós-instalação a qualquer momento usando a CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Post-install functions use `definePostInstallLogicFunction()` — a specialized variant that omits trigger settings (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* The handler receives an `InstallPayload` with `{ previousVersion?: string; newVersion: string }` — `newVersion` is the version being installed, and `previousVersion` is the version that was previously installed (or `undefined` on a fresh install). Use these values to distinguish fresh installs from upgrades and to run version-specific migration logic.
|
||||
* **When the hook runs**: on fresh installs only, by default. Pass `shouldRunOnVersionUpgrade: true` if you also want it to run when the app is upgraded from a previous version. When omitted, the flag defaults to `false` and upgrades skip the hook.
|
||||
* **Execution model — async by default, sync opt-in**: the `shouldRunSynchronously` flag controls *how* post-install is executed.
|
||||
* `shouldRunSynchronously: false` *(default)* — the hook is **enqueued on the message queue** with `retryLimit: 3` and runs asynchronously in a worker. The install response returns as soon as the job is enqueued, so a slow or failing handler does not block the caller. The worker will retry up to three times. **Use this for long-running jobs** — seeding large datasets, calling slow third-party APIs, provisioning external resources, anything that might exceed a reasonable HTTP response window.
|
||||
* `shouldRunSynchronously: true` — the hook is executed **inline during the install flow** (same executor as pre-install). The install request blocks until the handler finishes, and if it throws, the install caller receives a `POST_INSTALL_ERROR`. No automatic retries. **Use this for fast, must-complete-before-response work** — for example, emitting a validation error to the user, or quick setup that the client will rely on immediately after the install call returns. Keep in mind the metadata migration has already been applied by the time post-install runs, so a sync-mode failure does **not** roll back the schema changes — it only surfaces the error.
|
||||
* Make sure your handler is idempotent. In async mode the queue may retry up to three times; in either mode the hook may run again on upgrades when `shouldRunOnVersionUpgrade: true`.
|
||||
* The environment variables `APPLICATION_ID`, `APP_ACCESS_TOKEN`, and `API_URL` are available inside the handler (same as any other logic function), so you can call the Twenty API with an application access token scoped to your app.
|
||||
* Only one post-install function is allowed per application. The manifest build will error if more than one is detected.
|
||||
* The function's `universalIdentifier`, `shouldRunOnVersionUpgrade`, and `shouldRunSynchronously` are automatically attached to the application manifest under the `postInstallLogicFunction` field during the build — you do not need to reference them in [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
|
||||
* The default timeout is set to 300 seconds (5 minutes) to allow for longer setup tasks like data seeding.
|
||||
* **Not executed in dev mode**: when an app is registered locally (via `yarn twenty dev`), the server skips the install flow entirely and syncs files directly through the CLI watcher — so post-install never runs in dev mode, regardless of `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` to trigger it manually against a running workspace.
|
||||
Pontos-chave:
|
||||
* As funções de pós-instalação usam `definePostInstallLogicFunction()` — uma variante especializada que omite as configurações de gatilho (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* O manipulador recebe um `InstallPayload` com `{ previousVersion?: string; newVersion: string }` — `newVersion` é a versão que está sendo instalada, e `previousVersion` é a versão que foi instalada anteriormente (ou `undefined` em uma instalação nova). Use esses valores para distinguir instalações novas de atualizações e para executar lógica de migração específica da versão.
|
||||
* **Quando o hook é executado**: apenas em instalações novas, por padrão. Passe `shouldRunOnVersionUpgrade: true` se você também quiser que ele seja executado quando o app for atualizado a partir de uma versão anterior. Quando omitida, a flag tem valor padrão `false` e as atualizações ignoram o hook.
|
||||
* **Modelo de execução — assíncrono por padrão, síncrono opcional**: a flag `shouldRunSynchronously` controla *como* a pós-instalação é executada.
|
||||
* `shouldRunSynchronously: false` *(padrão)* — o hook é **enfileirado na fila de mensagens** com `retryLimit: 3` e é executado de forma assíncrona em um worker. A resposta da instalação retorna assim que o job é enfileirado, então um manipulador lento ou com falha não bloqueia quem chamou. O worker tentará novamente até três vezes. **Use isto para jobs de longa duração** — popular grandes conjuntos de dados, chamar APIs de terceiros lentas, provisionar recursos externos, qualquer coisa que possa exceder uma janela razoável de resposta HTTP.
|
||||
* `shouldRunSynchronously: true` — o hook é executado **inline durante o fluxo de instalação** (mesmo executor da pré-instalação). A requisição de instalação bloqueia até o manipulador terminar e, se ele lançar uma exceção, quem chamou a instalação recebe um `POST_INSTALL_ERROR`. Sem novas tentativas automáticas. **Use isto para trabalhos rápidos que precisam ser concluídos antes da resposta** — por exemplo, emitir um erro de validação para o usuário ou fazer uma configuração rápida da qual o cliente dependerá imediatamente após a chamada de instalação retornar. Tenha em mente que a migração de metadados já foi aplicada quando a pós-instalação é executada, então uma falha no modo síncrono **não** reverte as alterações de esquema — ela apenas expõe o erro.
|
||||
* Garanta que seu manipulador seja idempotente. No modo assíncrono, a fila pode tentar novamente até três vezes; em qualquer modo, o hook pode ser executado novamente em atualizações quando `shouldRunOnVersionUpgrade: true`.
|
||||
* As variáveis de ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` estão disponíveis dentro do manipulador (assim como em qualquer outra função de lógica), então você pode chamar a API da Twenty com um token de acesso de aplicativo com escopo para o seu app.
|
||||
* É permitida apenas uma função de pós-instalação por app. A geração do manifesto apresentará erro se mais de uma for detectada.
|
||||
* O `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` da função são anexados automaticamente ao manifesto do aplicativo no campo `postInstallLogicFunction` durante o build — você não precisa referenciá-los em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
|
||||
* O tempo limite padrão é definido como 300 segundos (5 minutos) para permitir tarefas de configuração mais longas, como o pré-carregamento de dados.
|
||||
* **Não executado no modo de desenvolvimento**: quando um app é registrado localmente (via `yarn twenty dev`), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` para acioná-lo manualmente em um workspace em execução.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Runs before the workspace metadata migration is applied">
|
||||
<Accordion title="definePreInstallLogicFunction" description="É executada antes que a migração de metadados do workspace seja aplicada">
|
||||
|
||||
A pre-install function runs automatically during installation, **before the workspace metadata migration is applied**. It shares the same payload shape as post-install (`InstallPayload`), but it is positioned earlier in the install flow so it can prepare state that the upcoming migration depends on — typical uses include backing up data, validating compatibility with the new schema, or archiving records that are about to be restructured or dropped.
|
||||
Uma função de pré-instalação é executada automaticamente durante a instalação, **antes que a migração de metadados do workspace seja aplicada**. Ela compartilha o mesmo formato de payload que a pós-instalação (`InstallPayload`), mas está posicionada mais cedo no fluxo de instalação para poder preparar o estado do qual a próxima migração depende — usos típicos incluem fazer backup de dados, validar a compatibilidade com o novo esquema ou arquivar registros que estão prestes a ser reestruturados ou removidos.
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -84,35 +84,35 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the pre-install function at any time using the CLI:
|
||||
Você também pode executar manualmente a função de pré-instalação a qualquer momento usando a CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --preInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Pre-install functions use `definePreInstallLogicFunction()` — same specialized config as post-install, just attached to a different lifecycle slot.
|
||||
* Both pre- and post-install handlers receive the same `InstallPayload` type: `{ previousVersion?: string; newVersion: string }`. Import it once and reuse it for both hooks.
|
||||
* **When the hook runs**: positioned just before the workspace metadata migration (`synchronizeFromManifest`). Before executing, the server runs a purely additive "pared-down sync" that registers the **new** version's pre-install function in the workspace metadata — nothing else is touched — and then executes it. Because this sync is additive-only, the previous version's objects, fields, and data are still intact when your handler runs: you can safely read and back up pre-migration state.
|
||||
* **Execution model**: pre-install is executed **synchronously** and **blocks the install**. If the handler throws, the install is aborted before any schema changes are applied — the workspace stays on the previous version in a consistent state. This is intentional: pre-install is your last chance to refuse a risky upgrade.
|
||||
* As with post-install, only one pre-install function is allowed per application. It is attached to the application manifest under `preInstallLogicFunction` automatically during the build.
|
||||
* **Not executed in dev mode**: same as post-install — the install flow is skipped entirely for locally-registered apps, so pre-install never runs under `yarn twenty dev`. Use `yarn twenty exec --preInstall` to trigger it manually.
|
||||
Pontos-chave:
|
||||
* Funções de pré-instalação usam `definePreInstallLogicFunction()` — a mesma configuração especializada da pós-instalação, apenas anexada a um ponto diferente do ciclo de vida.
|
||||
* Os manipuladores de pré e pós-instalação recebem o mesmo tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importe-o uma vez e reutilize-o para ambos os hooks.
|
||||
* **Quando o hook é executado**: posicionado imediatamente antes da migração de metadados do workspace (`synchronizeFromManifest`). Antes de executar, o servidor realiza uma "sincronização simplificada" puramente aditiva que registra a função de pré-instalação da **nova** versão nos metadados do workspace — nada mais é alterado — e então a executa. Como essa sincronização é apenas aditiva, os objetos, campos e dados da versão anterior ainda estão intactos quando seu manipulador é executado: você pode ler e fazer backup com segurança do estado pré-migração.
|
||||
* **Modelo de execução**: a pré-instalação é executada **de forma síncrona** e **bloqueia a instalação**. Se o manipulador lançar uma exceção, a instalação é abortada antes que quaisquer alterações de esquema sejam aplicadas — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada.
|
||||
* Assim como na pós-instalação, é permitida apenas uma função de pré-instalação por app. Ela é anexada ao manifesto do aplicativo sob `preInstallLogicFunction` automaticamente durante o build.
|
||||
* **Não é executada no modo de desenvolvimento**: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com `yarn twenty dev`. Use `yarn twenty exec --preInstall` para acioná-lo manualmente.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Pre-install vs post-install: when to use which" description="Choosing the right install hook">
|
||||
<Accordion title="Pré-instalação vs pós-instalação: quando usar cada um" description="Escolhendo o hook de instalação correto">
|
||||
|
||||
Both hooks are part of the same install flow and receive the same `InstallPayload`. The difference is **when** they run relative to the workspace metadata migration, and that changes what data they can safely touch.
|
||||
Ambos os hooks fazem parte do mesmo fluxo de instalação e recebem o mesmo `InstallPayload`. A diferença é **quando** eles são executados em relação à migração de metadados do workspace, e isso muda quais dados eles podem manipular com segurança.
|
||||
|
||||
Pre-install is always **synchronous** (it blocks the install and can abort it). Post-install is **asynchronous by default** — enqueued on a worker with automatic retries — but can opt into synchronous execution with `shouldRunSynchronously: true`. See the `definePostInstallLogicFunction` accordion above for when to use each mode.
|
||||
A pré-instalação é sempre **síncrona** (ela bloqueia a instalação e pode abortá-la). A pós-instalação é **assíncrona por padrão** — enfileirada em um worker com novas tentativas automáticas — mas pode optar por execução síncrona com `shouldRunSynchronously: true`. Veja o acordeão `definePostInstallLogicFunction` acima para saber quando usar cada modo.
|
||||
|
||||
**Use `post-install` for anything that needs the new schema to exist.** This is the common case:
|
||||
**Use `post-install` para qualquer coisa que precise que o novo esquema exista.** Este é o caso mais comum:
|
||||
|
||||
* Seeding default data (creating initial records, default views, demo content) against newly-added objects and fields.
|
||||
* Registering webhooks with third-party services now that the app has its credentials.
|
||||
* Calling your own API to finish setup that depends on the synchronized metadata.
|
||||
* Idempotent "ensure this exists" logic that should reconcile state on every upgrade — combine with `shouldRunOnVersionUpgrade: true`.
|
||||
* Popular dados padrão (criando registros iniciais, visualizações padrão, conteúdo de demonstração) em objetos e campos recém-adicionados.
|
||||
* Registrar webhooks com serviços de terceiros agora que o app tem suas credenciais.
|
||||
* Chamar sua própria API para finalizar a configuração que depende dos metadados sincronizados.
|
||||
* Lógica idempotente de "garantir que isso exista" que deve reconciliar o estado em cada atualização — combine com `shouldRunOnVersionUpgrade: true`.
|
||||
|
||||
Example — seed a default `PostCard` record after install:
|
||||
Exemplo — popular um registro `PostCard` padrão após a instalação:
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -137,14 +137,14 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Use `pre-install` when a migration would otherwise destroy or corrupt existing data.** Because pre-install runs against the *previous* schema and its failure rolls back the upgrade, it is the right place for anything risky:
|
||||
**Use `pre-install` quando uma migração, de outra forma, destruiria ou corromperia dados existentes.** Como a pré-instalação roda contra o esquema *anterior* e sua falha reverte a atualização, é o lugar certo para qualquer coisa arriscada:
|
||||
|
||||
* **Backing up data that is about to be dropped or restructured** — e.g. you are removing a field in v2 and need to copy its values into another field or export them to storage before the migration runs.
|
||||
* **Archiving records that a new constraint would invalidate** — e.g. a field is becoming `NOT NULL` and you need to delete or fix rows with null values first.
|
||||
* **Validating compatibility and refusing the upgrade if the current data cannot be migrated cleanly** — throw from the handler and the install aborts with no changes applied. This is safer than discovering the incompatibility mid-migration.
|
||||
* **Renaming or rekeying data** ahead of a schema change that would lose the association.
|
||||
* **Fazer backup de dados que estão prestes a ser removidos ou reestruturados** — por exemplo, você está removendo um campo na v2 e precisa copiar seus valores para outro campo ou exportá-los para um armazenamento antes que a migração seja executada.
|
||||
* **Arquivar registros que uma nova restrição invalidaria** — por exemplo, um campo está se tornando `NOT NULL` e você precisa excluir ou corrigir linhas com valores nulos primeiro.
|
||||
* **Validar a compatibilidade e recusar a atualização se os dados atuais não puderem ser migrados de forma limpa** — lance uma exceção no manipulador e a instalação é abortada sem alterações aplicadas. Isto é mais seguro do que descobrir a incompatibilidade no meio da migração.
|
||||
* **Renomear ou reatribuir chaves de dados** antes de uma alteração de esquema que perderia a associação.
|
||||
|
||||
Example — archive records before a destructive migration:
|
||||
Exemplo — arquivar registros antes de uma migração destrutiva:
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -186,20 +186,20 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Rule of thumb:**
|
||||
**Regra geral:**
|
||||
|
||||
| You want to... | Use |
|
||||
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| Seed default data, configure the workspace, register external resources | `post-install` |
|
||||
| Run long-running seeding or third-party calls that shouldn't block the install response | `post-install` (default — `shouldRunSynchronously: false`, with worker retries) |
|
||||
| Run fast setup that the caller will rely on immediately after the install call returns | `post-install` with `shouldRunSynchronously: true` |
|
||||
| Read or back up data that the upcoming migration would lose | `pre-install` |
|
||||
| Reject an upgrade that would corrupt existing data | `pre-install` (throw from the handler) |
|
||||
| Run reconciliation on every upgrade | `post-install` with `shouldRunOnVersionUpgrade: true` |
|
||||
| Do one-off setup on the first install only | `post-install` with `shouldRunOnVersionUpgrade: false` (default) |
|
||||
| Você quer... | Usar |
|
||||
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| Popular dados padrão, configurar o workspace, registrar recursos externos | `post-install` |
|
||||
| Executar processos longos de popular dados ou chamadas a terceiros que não devem bloquear a resposta da instalação | `post-install` (padrão — `shouldRunSynchronously: false`, com novas tentativas do worker) |
|
||||
| Executar uma configuração rápida da qual o chamador dependerá imediatamente após o retorno da chamada de instalação | `post-install` com `shouldRunSynchronously: true` |
|
||||
| Ler ou fazer backup de dados que a próxima migração perderia | `pre-install` |
|
||||
| Rejeitar uma atualização que corromperia dados existentes | `pre-install` (lançar uma exceção no manipulador) |
|
||||
| Executar reconciliação em cada atualização | `post-install` com `shouldRunOnVersionUpgrade: true` |
|
||||
| Fazer uma configuração única apenas na primeira instalação | `post-install` com `shouldRunOnVersionUpgrade: false` (padrão) |
|
||||
|
||||
<Note>
|
||||
If in doubt, default to **post-install**. Only reach for pre-install when the migration itself is destructive and you need to intercept the previous state before it is gone.
|
||||
Em caso de dúvida, use **post-install** como padrão. Recurra à pré-instalação somente quando a própria migração for destrutiva e você precisar interceptar o estado anterior antes que ele desapareça.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Configure the app itself — its identity, default permissions, and what runs at install time.
|
||||
title: Visão Geral
|
||||
description: Configure a própria aplicação — a sua identidade, permissões predefinidas e o que é executado no momento da instalação.
|
||||
icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
A Twenty app's **config layer** is what describes the app *to the platform* — its identity, the permissions it holds, and the code that runs during install or upgrade. These declarations don't add new data shapes or runtime behavior; they tell Twenty *who the app is* and *how to set it up*.
|
||||
A **camada de configuração** de uma aplicação Twenty é o que descreve a aplicação *para a plataforma* — a sua identidade, as permissões que detém e o código que é executado durante a instalação ou atualização. Estas declarações não adicionam novos formatos de dados nem comportamento em tempo de execução; dizem à Twenty *quem é a aplicação* e *como configurá-la*.
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
@@ -26,26 +26,26 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## Nesta seção
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Application Config" icon="rocket" href="/l/pt/developers/extend/apps/config/application">
|
||||
`defineApplication` — identity, default role, variables, marketplace metadata.
|
||||
<Card title="Configuração da aplicação" icon="rocket" href="/l/pt/developers/extend/apps/config/application">
|
||||
`defineApplication` — identidade, função predefinida, variáveis, metadados do marketplace.
|
||||
</Card>
|
||||
<Card title="Roles & Permissions" icon="shield-halved" href="/l/pt/developers/extend/apps/config/roles">
|
||||
`defineRole` — declare what your app's logic functions can read and write.
|
||||
<Card title="Funções e Permissões" icon="shield-halved" href="/l/pt/developers/extend/apps/config/roles">
|
||||
`defineRole` — declara o que as funções de lógica da sua aplicação podem ler e escrever.
|
||||
</Card>
|
||||
<Card title="Install Hooks" icon="wrench" href="/l/pt/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` and `definePostInstallLogicFunction` — back up data, seed defaults, validate upgrades.
|
||||
<Card title="Hooks de instalação" icon="wrench" href="/l/pt/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` e `definePostInstallLogicFunction` — fazem cópias de segurança dos dados, pré-preenchem valores padrão, validam atualizações.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## How the pieces relate
|
||||
## Como as peças se relacionam
|
||||
|
||||
* **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).
|
||||
* A **Aplicação** é o ponto de entrada. Cada aplicação tem exatamente uma chamada `defineApplication()`, e esta aponta para uma **Função** como predefinida.
|
||||
* A **Função** controla o que as funções de lógica e os componentes de interface da aplicação podem ler e escrever. Siga o princípio do menor privilégio: conceda apenas as permissões de que o seu código realmente necessita.
|
||||
* Os **Hooks de instalação** são executados durante a instalação ou atualização — o pré-instalação antes da migração de metadados (para que possa recusar uma atualização arriscada) e o pós-instalação depois da migração (para que possa pré-preencher dados padrão com base no novo esquema).
|
||||
|
||||
<Note>
|
||||
Install hooks share the [logic function](/l/pt/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).
|
||||
Os hooks de instalação partilham o ambiente de execução de [função de lógica](/l/pt/developers/extend/apps/logic/logic-functions) — a mesma assinatura de handler, as mesmas variáveis de ambiente, o mesmo cliente de API tipado — mas são declarados com as suas próprias funções "define" e vivem fora do modelo de disparo normal (HTTP, cron, eventos de base de dados).
|
||||
</Note>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
---
|
||||
title: Public Assets
|
||||
description: Ship static files — images, icons, fonts — alongside your app via the public/ folder.
|
||||
title: Recursos públicos
|
||||
description: Envie arquivos estáticos — imagens, ícones, fontes — junto com seu app por meio da pasta public/.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
The `public/` folder at the root of your app holds static files — images, icons, fonts, or any other assets your app needs at runtime. These files are automatically included in builds, synced during dev mode, and uploaded to the server.
|
||||
A pasta `public/` na raiz do seu app contém arquivos estáticos — imagens, ícones, fontes ou quaisquer outros recursos de que seu app precisa em tempo de execução. Esses arquivos são incluídos automaticamente nas compilações, sincronizados durante o modo de desenvolvimento e enviados para o servidor.
|
||||
|
||||
Files placed in `public/` are:
|
||||
Arquivos colocados em `public/` são:
|
||||
|
||||
* **Publicly accessible** — once synced to the server, assets are served at a public URL. No authentication is needed to access them.
|
||||
* **Available in front components** — use asset URLs to display images, icons, or any media inside your React components.
|
||||
* **Available in logic functions** — reference asset URLs in emails, API responses, or any server-side logic.
|
||||
* **Used for marketplace metadata** — the `logoUrl` and `screenshots` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published.
|
||||
* **Auto-synced in dev mode** — when you add, update, or delete a file in `public/`, it is synced to the server automatically. No restart needed.
|
||||
* **Included in builds** — `yarn twenty build` bundles all public assets into the distribution output.
|
||||
* **Publicamente acessíveis** — depois de sincronizados com o servidor, os recursos são servidos em uma URL pública. Não é necessária autenticação para acessá-los.
|
||||
* **Disponíveis em componentes de front-end** — use URLs de recursos para exibir imagens, ícones ou qualquer mídia dentro de seus componentes React.
|
||||
* **Disponíveis em funções lógicas** — referencie URLs de recursos em e-mails, respostas de API ou qualquer lógica no lado do servidor.
|
||||
* **Usados para metadados do marketplace** — os campos `logoUrl` e `screenshots` em `defineApplication()` referenciam arquivos desta pasta (por exemplo, `public/logo.png`). Eles são exibidos no marketplace quando seu app é publicado.
|
||||
* **Sincronizados automaticamente no modo de desenvolvimento** — quando você adiciona, atualiza ou exclui um arquivo em `public/`, ele é sincronizado automaticamente com o servidor. Não é necessário reiniciar.
|
||||
* **Incluídos nas compilações** — `yarn twenty build` agrupa todos os recursos públicos na saída de distribuição.
|
||||
|
||||
## Accessing public assets with `getPublicAssetUrl`
|
||||
## Acessando recursos públicos com `getPublicAssetUrl`
|
||||
|
||||
Use the `getPublicAssetUrl` helper from `twenty-sdk` to get the full URL of a file in your `public/` directory. It works in both **logic functions** and **front components**.
|
||||
Use o helper `getPublicAssetUrl` de `twenty-sdk` para obter a URL completa de um arquivo no seu diretório `public/`. Funciona tanto em **funções lógicas** quanto em **componentes de front-end**.
|
||||
|
||||
**In a logic function:**
|
||||
**Em uma função lógica:**
|
||||
|
||||
```ts src/logic-functions/send-invoice.ts
|
||||
import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -44,7 +44,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**In a front component:**
|
||||
**Em um componente de front-end:**
|
||||
|
||||
```tsx src/front-components/company-card.tsx
|
||||
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -56,4 +56,4 @@ export default defineFrontComponent(() => {
|
||||
});
|
||||
```
|
||||
|
||||
The `path` argument is relative to your app's `public/` folder. Both `getPublicAssetUrl('logo.png')` and `getPublicAssetUrl('public/logo.png')` resolve to the same URL — the `public/` prefix is stripped automatically if present.
|
||||
O argumento `path` é relativo à pasta `public/` do seu app. Tanto `getPublicAssetUrl('logo.png')` quanto `getPublicAssetUrl('public/logo.png')` resolvem para a mesma URL — o prefixo `public/` é removido automaticamente, se presente.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Roles & Permissions
|
||||
description: Declare what objects and fields your app's logic functions and front components can read and write.
|
||||
title: Funções e Permissões
|
||||
description: Declare quais objetos e campos as funções de lógica e os componentes de front-end do seu app podem ler e gravar.
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
A **role** is a permission set: which objects an app can read or write, which fields it can see, and which platform-level capabilities it can use. Every app's logic functions and front components inherit the permissions of the role declared as `defaultRoleUniversalIdentifier` in [`defineApplication`](/l/pt/developers/extend/apps/config/application).
|
||||
Um **papel** é um conjunto de permissões: quais objetos um app pode ler ou gravar, quais campos ele pode ver e quais recursos em nível de plataforma ele pode usar. Todas as funções de lógica e os componentes de front-end do app herdam as permissões do papel declarado como `defaultRoleUniversalIdentifier` em [`defineApplication`](/l/pt/developers/extend/apps/config/application).
|
||||
|
||||
```ts src/roles/restricted-company-role.ts
|
||||
import {
|
||||
@@ -49,9 +49,9 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
## The default function role
|
||||
## Papel de função padrão
|
||||
|
||||
When you scaffold a new app, the CLI creates a default role file:
|
||||
Ao criar um novo app com o scaffold, a CLI cria um arquivo de papel padrão:
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineRole, PermissionFlag } from 'twenty-sdk/define';
|
||||
@@ -77,14 +77,14 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
This role's `universalIdentifier` is referenced from `application-config.ts` as `defaultRoleUniversalIdentifier`:
|
||||
O `universalIdentifier` desse papel é referenciado em `application-config.ts` como `defaultRoleUniversalIdentifier`:
|
||||
|
||||
* **`*.role.ts`** declares what the role can do.
|
||||
* **`application-config.ts`** points to that role so your functions inherit its permissions.
|
||||
* **`*.role.ts`** declara o que o papel pode fazer.
|
||||
* **`application-config.ts`** aponta para esse papel para que suas funções herdem suas permissões.
|
||||
|
||||
## Best practices
|
||||
## Melhores Práticas
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it — the default grants broad read access, which is rarely what you want in production.
|
||||
* Replace `objectPermissions` and `fieldPermissions` with the exact objects and fields your functions actually need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal.
|
||||
* See a working example: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
* Comece a partir do papel gerado pelo scaffold e, em seguida, restrinja-o progressivamente — o padrão concede amplo acesso de leitura, o que raramente é o que você quer em produção.
|
||||
* Substitua `objectPermissions` e `fieldPermissions` pelos objetos e campos de que suas funções realmente precisam.
|
||||
* `permissionFlags` controlam o acesso a recursos em nível de plataforma. Mantenha-os no mínimo necessário.
|
||||
* Veja um exemplo funcional: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Extending Objects
|
||||
description: Add fields to standard Twenty objects (Person, Company, …) or to objects from other apps using defineField.
|
||||
title: Estendendo objetos
|
||||
description: Adicione campos a objetos padrão do Twenty (Person, Company, …) ou a objetos de outros apps usando defineField.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Use `defineField()` to add a field to an object you don't own — a standard Twenty object like Person or Company, or an object shipped by another installed app. Unlike inline fields declared inside [`defineObject`](/l/pt/developers/extend/apps/data/objects), standalone fields require an `objectUniversalIdentifier` to specify which object they extend.
|
||||
Use `defineField()` para adicionar um campo a um objeto que você não possui — um objeto padrão do Twenty como Person ou Company, ou um objeto disponibilizado por outro app instalado. Ao contrário dos campos inline declarados dentro de [`defineObject`](/l/pt/developers/extend/apps/data/objects), os campos independentes exigem um `objectUniversalIdentifier` para especificar qual objeto eles estendem.
|
||||
|
||||
```ts src/fields/company-loyalty-tier.field.ts
|
||||
import { defineField, FieldType } from 'twenty-sdk/define';
|
||||
@@ -24,9 +24,9 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Pontos-chave
|
||||
|
||||
* `objectUniversalIdentifier` identifies the target object. For standard Twenty objects, import the constant from `twenty-sdk`:
|
||||
* `objectUniversalIdentifier` identifica o objeto de destino. Para objetos padrão do Twenty, importe a constante de `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
@@ -37,12 +37,12 @@ export default defineField({
|
||||
// …
|
||||
```
|
||||
|
||||
* When defining fields **inline inside `defineObject()`**, you do **not** need `objectUniversalIdentifier` — it's inherited from the parent object.
|
||||
* Ao definir campos **inline dentro de `defineObject()`**, você **não** precisa de `objectUniversalIdentifier` — ele é herdado do objeto pai.
|
||||
|
||||
* `defineField()` is the only way to add fields to objects you didn't create with `defineObject()`.
|
||||
* `defineField()` é a única forma de adicionar campos a objetos que você não criou com `defineObject()`.
|
||||
|
||||
* File location is up to you. The convention is `src/fields/\<name>.field.ts`, but the SDK detects fields anywhere in `src/`.
|
||||
* A localização do arquivo fica a seu critério. A convenção é `src/fields/\<name>.field.ts`, mas o SDK detecta campos em qualquer lugar dentro de `src/`.
|
||||
|
||||
## Adding a relation to an existing object
|
||||
## Adicionando uma relação a um objeto existente
|
||||
|
||||
To add a relation field (e.g. linking your custom object to a standard `Person`), use `defineField()` with `FieldType.RELATION`. The pattern is the same as for inline relations but with `objectUniversalIdentifier` set explicitly. See [Relations](/l/pt/developers/extend/apps/data/relations) for the bidirectional pattern.
|
||||
Para adicionar um campo de relação (por exemplo, vinculando seu objeto personalizado a um `Person` padrão), use `defineField()` com `FieldType.RELATION`. O padrão é o mesmo que para relações inline, mas com `objectUniversalIdentifier` definido explicitamente. Veja [Relações](/l/pt/developers/extend/apps/data/relations) para o padrão bidirecional.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Objetos
|
||||
description: Declare new record types — custom tables with their own fields — using defineObject.
|
||||
description: Declare novos tipos de registro — tabelas personalizadas com seus próprios campos — usando defineObject.
|
||||
icon: tabela
|
||||
---
|
||||
|
||||
Custom **objects** are new record types your app adds to a workspace — Post Card, Invoice, Subscription, anything specific to your domain. Each object declares its schema (fields, relations, default values) and a stable universal identifier that survives across syncs and deploys.
|
||||
**Objetos** personalizados são novos tipos de registro que o seu app adiciona a um espaço de trabalho — cartão‑postal, fatura, assinatura, qualquer coisa específica do seu domínio. Cada objeto declara seu esquema (campos, relações, valores padrão) e um identificador universal estável que persiste entre sincronizações e implantações.
|
||||
|
||||
```ts src/objects/post-card.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
@@ -79,15 +79,15 @@ export default defineObject({
|
||||
* O `universalIdentifier` deve ser exclusivo e estável entre implantações.
|
||||
* Cada campo requer `name`, `type`, `label` e seu próprio `universalIdentifier` estável.
|
||||
* O array `fields` é opcional — você pode definir objetos sem campos personalizados.
|
||||
* Inline fields defined here do **not** need an `objectUniversalIdentifier` — it's inherited from the parent object. Use [`defineField()`](/l/pt/developers/extend/apps/data/extending-objects) to add fields to objects you don't own.
|
||||
* You can scaffold new objects with `yarn twenty add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/l/pt/developers/extend/apps/getting-started/scaffolding).
|
||||
* Campos inline definidos aqui **não** precisam de `objectUniversalIdentifier` — ele é herdado do objeto pai. Use [`defineField()`](/l/pt/developers/extend/apps/data/extending-objects) para adicionar campos a objetos que não pertencem a você.
|
||||
* Você pode criar novos objetos com `yarn twenty add object`, que orienta você sobre nomeação, campos e relacionamentos. Veja [Arquitetura → Scaffolding de entidades](/l/pt/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
<Note>
|
||||
**Base fields are added automatically.** When you define a custom object, Twenty creates standard fields like `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, and `deletedAt` for you. You don't need to declare them in your `fields` array — only your custom fields. You can override a default field by declaring one with the same name, but this is rarely a good idea.
|
||||
**Os campos base são adicionados automaticamente.** Quando você define um objeto personalizado, o Twenty cria campos padrão como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt` para você. Você não precisa declará‑los no seu array `fields` — apenas seus campos personalizados. Você pode substituir um campo padrão declarando um com o mesmo nome, mas isso raramente é uma boa ideia.
|
||||
</Note>
|
||||
|
||||
## O que vem depois
|
||||
|
||||
* **Connect this object to others** — see [Relations](/l/pt/developers/extend/apps/data/relations) for the bidirectional relation pattern.
|
||||
* **Add fields to objects from other apps** — see [Extending Objects](/l/pt/developers/extend/apps/data/extending-objects) for `defineField()`.
|
||||
* **Display this object in the UI** — see [Views](/l/pt/developers/extend/apps/layout/views) and [Navigation Menu Items](/l/pt/developers/extend/apps/layout/navigation-menu-items) to put it in the sidebar.
|
||||
* **Conecte este objeto a outros** — veja [Relações](/l/pt/developers/extend/apps/data/relations) para o padrão de relação bidirecional.
|
||||
* **Adicione campos a objetos de outros apps** — veja [Extensão de objetos](/l/pt/developers/extend/apps/data/extending-objects) para `defineField()`.
|
||||
* **Exiba este objeto na interface** — veja [Views](/l/pt/developers/extend/apps/layout/views) e [Itens do menu de navegação](/l/pt/developers/extend/apps/layout/navigation-menu-items) para colocá‑lo na barra lateral.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Shape the data your app adds to a workspace — objects, fields, and relations.
|
||||
title: Visão Geral
|
||||
description: Modele os dados que seu app adiciona a um workspace — objetos, campos e relações.
|
||||
icon: database
|
||||
---
|
||||
|
||||
A Twenty app's **data layer** is the data your app *adds* to a workspace — the new record types it declares, the columns it adds to existing objects, and how those records connect to each other.
|
||||
A **camada de dados** de um app Twenty é o conjunto de dados que seu app *adiciona* a um workspace — os novos tipos de registros que ele declara, as colunas que adiciona a objetos existentes e como esses registros se conectam entre si.
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────┐
|
||||
@@ -23,30 +23,30 @@ A Twenty app's **data layer** is the data your app *adds* to a workspace — the
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## Nesta seção
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Objects" icon="table" href="/l/pt/developers/extend/apps/data/objects">
|
||||
`defineObject` — declare new record types with their own fields.
|
||||
<Card title="Objetos" icon="tabela" href="/l/pt/developers/extend/apps/data/objects">
|
||||
`defineObject` — declare novos tipos de registros com seus próprios campos.
|
||||
</Card>
|
||||
<Card title="Extending Objects" icon="wand-magic-sparkles" href="/l/pt/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — add fields to standard or other apps' objects.
|
||||
<Card title="Estendendo objetos" icon="wand-magic-sparkles" href="/l/pt/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — adicione campos a objetos padrão ou de outros apps.
|
||||
</Card>
|
||||
<Card title="Relations" icon="diagram-project" href="/l/pt/developers/extend/apps/data/relations">
|
||||
Bidirectional `MANY_TO_ONE` / `ONE_TO_MANY` connections between objects.
|
||||
<Card title="Relações" icon="diagram-project" href="/l/pt/developers/extend/apps/data/relations">
|
||||
Conexões bidirecionais `MANY_TO_ONE` / `ONE_TO_MANY` entre objetos.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Entities at a glance
|
||||
## Entidades em resumo
|
||||
|
||||
| Entity | Purpose | Defined with |
|
||||
| ------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Object** | A new custom record type (e.g. PostCard, Invoice) with its own fields | `defineObject()` |
|
||||
| **Field** | A column on an object. Standalone fields can extend objects you didn't create (e.g. add `loyaltyTier` to Company) | `defineField()` |
|
||||
| **Relation** | A bidirectional link between two objects — both sides declared as fields | `defineField()` with `FieldType.RELATION` |
|
||||
| Entidade | Finalidade | Definido com |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
|
||||
| **Objeto** | Um novo tipo de registro personalizado (por exemplo, PostCard, Invoice) com seus próprios campos | `defineObject()` |
|
||||
| **Campo** | Uma coluna em um objeto. Campos independentes podem estender objetos que você não criou (por exemplo, adicionar `loyaltyTier` a Company) | `defineField()` |
|
||||
| **Relação** | Um vínculo bidirecional entre dois objetos — ambos os lados declarados como campos | `defineField()` com `FieldType.RELATION` |
|
||||
|
||||
The SDK detects these via AST analysis at build time, so file organization is up to you — the convention is `src/objects/` and `src/fields/`. Stable `universalIdentifier` UUIDs tie everything together across deploys.
|
||||
O SDK detecta esses elementos por meio de análise de AST em tempo de build, então a organização dos arquivos fica a seu critério — a convenção é `src/objects/` e `src/fields/`. UUIDs `universalIdentifier` estáveis conectam tudo em implantações diferentes.
|
||||
|
||||
<Note>
|
||||
Looking for **Application Config** or **Roles & Permissions**? Those describe the app itself rather than the data it adds — they live under [Config](/l/pt/developers/extend/apps/config/overview). Looking for **Connections** (Linear, GitHub, Slack OAuth)? Those exist to be called *from* logic functions and live under [Logic](/l/pt/developers/extend/apps/logic/connections).
|
||||
Procurando por **Application Config** ou **Roles & Permissions**? Esses descrevem o próprio app em vez dos dados que ele adiciona — eles ficam em [Config](/l/pt/developers/extend/apps/config/overview). Procurando por **Connections** (Linear, GitHub, Slack OAuth)? Essas existem para serem chamadas *a partir de* funções de lógica e ficam em [Logic](/l/pt/developers/extend/apps/logic/connections).
|
||||
</Note>
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
title: Relações
|
||||
description: Connect objects together with bidirectional MANY_TO_ONE / ONE_TO_MANY relations.
|
||||
description: Conecte objetos entre si com relações bidirecionais MANY_TO_ONE / ONE_TO_MANY.
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
Relations connect two objects together. In Twenty, relations are always **bidirectional** — every relation has two sides, and each side is declared as a field that references the other.
|
||||
As relações conectam dois objetos entre si. No Twenty, as relações são sempre **bidirecionais** — cada relação tem dois lados, e cada lado é declarado como um campo que faz referência ao outro.
|
||||
|
||||
| Tipo de relação | Descrição | Tem chave estrangeira? |
|
||||
| --------------- | ----------------------------------------------------------------- | ---------------------- |
|
||||
| `MANY_TO_ONE` | Muitos registros deste objeto apontam para um registro do destino | Sim (`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | Um registro deste objeto possui muitos registros do destino | No (the inverse side) |
|
||||
| `ONE_TO_MANY` | Um registro deste objeto possui muitos registros do destino | Não (o lado inverso) |
|
||||
|
||||
## How relations work
|
||||
## Como as relações funcionam
|
||||
|
||||
Every relation requires **two fields** that reference each other:
|
||||
Toda relação requer **dois campos** que façam referência um ao outro:
|
||||
|
||||
1. The **MANY_TO_ONE** side — lives on the object that holds the foreign key.
|
||||
2. The **ONE_TO_MANY** side — lives on the object that owns the collection.
|
||||
1. O lado **MANY_TO_ONE** — fica no objeto que contém a chave estrangeira.
|
||||
2. O lado **ONE_TO_MANY** — fica no objeto que possui a coleção.
|
||||
|
||||
Both fields use `FieldType.RELATION` and cross-reference each other via `relationTargetFieldMetadataUniversalIdentifier`.
|
||||
Ambos os campos usam `FieldType.RELATION` e fazem referência cruzada um ao outro via `relationTargetFieldMetadataUniversalIdentifier`.
|
||||
|
||||
## Example: Post Card has many Recipients
|
||||
## Exemplo: Um cartão postal tem muitos destinatários
|
||||
|
||||
A `PostCard` can be sent to many `PostCardRecipient` records. Each recipient belongs to exactly one post card.
|
||||
Um `PostCard` pode ser enviado para muitos registros `PostCardRecipient`. Cada destinatário pertence a exatamente um cartão postal.
|
||||
|
||||
**Step 1: Define the ONE_TO_MANY side on PostCard** (the "one" side):
|
||||
**Etapa 1: Defina o lado ONE_TO_MANY em PostCard** (o lado "um"):
|
||||
|
||||
```ts src/fields/post-card-recipients-on-post-card.field.ts
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
|
||||
@@ -51,7 +51,7 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Define the MANY_TO_ONE side on PostCardRecipient** (the "many" side — holds the foreign key):
|
||||
**Etapa 2: Defina o lado MANY_TO_ONE em PostCardRecipient** (o lado "muitos" — contém a chave estrangeira):
|
||||
|
||||
```ts src/fields/post-card-on-post-card-recipient.field.ts
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
|
||||
@@ -81,12 +81,12 @@ export default defineField({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Circular imports:** both relation fields reference each other's `universalIdentifier`. To avoid circular import issues, export your field IDs as named constants from each file and import them in the other. The build system resolves these at compile time.
|
||||
**Importações circulares:** ambos os campos de relação fazem referência ao `universalIdentifier` um do outro. Para evitar problemas de importação circular, exporte os IDs dos seus campos como constantes nomeadas de cada arquivo e importe-os no outro. O sistema de build resolve isso em tempo de compilação.
|
||||
</Note>
|
||||
|
||||
## Relating to standard objects
|
||||
## Relacionando a objetos padrão
|
||||
|
||||
To create a relation with a built-in Twenty object (Person, Company, etc.), use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
Para criar uma relação com um objeto integrado do Twenty (Person, Company, etc.), use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
|
||||
```ts src/fields/person-on-self-hosting-user.field.ts
|
||||
import {
|
||||
@@ -120,20 +120,20 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Relation field properties
|
||||
## Propriedades de campos de relação
|
||||
|
||||
| Property | Required | Description |
|
||||
| ------------------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `type` | Yes | Must be `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the target object |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the matching field on the target object |
|
||||
| `universalSettings.relationType` | Yes | `RelationType.MANY_TO_ONE` or `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | MANY_TO_ONE only | What happens when the referenced record is deleted: `CASCADE`, `SET_NULL`, `RESTRICT`, or `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | MANY_TO_ONE only | Database column name for the foreign key (e.g., `postCardId`) |
|
||||
| Propriedade | Obrigatório | Descrição |
|
||||
| ------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `type` | Sim | Deve ser `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Sim | O `universalIdentifier` do objeto de destino |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Sim | O `universalIdentifier` do campo correspondente no objeto de destino |
|
||||
| `universalSettings.relationType` | Sim | `RelationType.MANY_TO_ONE` ou `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | Apenas para MANY_TO_ONE | O que acontece quando o registro referenciado é excluído: `CASCADE`, `SET_NULL`, `RESTRICT` ou `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | Apenas para MANY_TO_ONE | Nome da coluna no banco de dados para a chave estrangeira (por exemplo, `postCardId`) |
|
||||
|
||||
## Inline relation fields
|
||||
## Campos de relação inline
|
||||
|
||||
You can also declare a relation directly inside [`defineObject`](/l/pt/developers/extend/apps/data/objects). When inline, omit `objectUniversalIdentifier` — it's inherited from the parent object:
|
||||
Você também pode declarar uma relação diretamente dentro de [`defineObject`](/l/pt/developers/extend/apps/data/objects). Quando estiver inline, omita `objectUniversalIdentifier` — ele é herdado do objeto pai:
|
||||
|
||||
```ts
|
||||
export default defineObject({
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: How Twenty apps work — entity model, sandboxing, and the install lifecycle.
|
||||
title: Conceitos
|
||||
description: Como os apps Twenty funcionam — modelo de entidade, sandboxing e ciclo de vida da instalação.
|
||||
icon: sitemap
|
||||
---
|
||||
|
||||
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
|
||||
As aplicações Twenty são pacotes TypeScript que estendem seu espaço de trabalho com objetos personalizados, lógica, componentes de UI e recursos de IA. Elas são executadas na plataforma Twenty com sandboxing completo e controles de permissão.
|
||||
|
||||
## How apps work
|
||||
## Como as aplicações funcionam
|
||||
|
||||
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace. These functions validate your configuration at build time and provide IDE autocompletion and type safety.
|
||||
Uma aplicação é uma coleção de **entidades** declaradas usando funções `defineEntity()` do pacote `twenty-sdk`. O SDK detecta essas declarações via análise de AST no momento da compilação e produz um **manifesto** — uma descrição completa do que seu aplicativo adiciona a um espaço de trabalho. Essas funções validam sua configuração em tempo de compilação e oferecem autocompletar na IDE e segurança de tipos.
|
||||
|
||||
```
|
||||
your-app/
|
||||
@@ -29,35 +29,35 @@ your-app/
|
||||
```
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
|
||||
**A organização de arquivos fica a seu critério.** A detecção de entidades é baseada em AST — o SDK encontra chamadas a `export default defineEntity(...)` independentemente de onde o arquivo esteja. A estrutura de pastas acima é uma convenção, não um requisito.
|
||||
</Note>
|
||||
|
||||
## Entity types
|
||||
## Tipos de entidade
|
||||
|
||||
| Entity | Purpose | Docs |
|
||||
| ------------------------ | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| **Application** | App identity, default role, variables | [Application Config](/l/pt/developers/extend/apps/config/application) |
|
||||
| **Role** | Permission sets on objects and fields | [Roles & Permissions](/l/pt/developers/extend/apps/config/roles) |
|
||||
| **Object** | Custom record types with fields | [Objects](/l/pt/developers/extend/apps/data/objects) |
|
||||
| **Field** | Add fields to objects from other apps | [Extending Objects](/l/pt/developers/extend/apps/data/extending-objects) |
|
||||
| **Relation** | Bidirectional links between objects | [Relations](/l/pt/developers/extend/apps/data/relations) |
|
||||
| **Logic Function** | Server-side TypeScript with triggers | [Logic Functions](/l/pt/developers/extend/apps/logic/logic-functions) |
|
||||
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/pt/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/pt/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Connection Provider** | OAuth credentials for third-party APIs | [Connections](/l/pt/developers/extend/apps/logic/connections) |
|
||||
| **View** | Pre-configured record list views | [Views](/l/pt/developers/extend/apps/layout/views) |
|
||||
| **Navigation Menu Item** | Custom sidebar entries | [Navigation Menu Items](/l/pt/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Page Layout** | Tabs and widgets on a record's detail page | [Page Layouts](/l/pt/developers/extend/apps/layout/page-layouts) |
|
||||
| **Front Component** | Sandboxed React UI inside Twenty | [Front Components](/l/pt/developers/extend/apps/layout/front-components) |
|
||||
| **Command Menu Item** | Quick actions and Cmd+K entries | [Command Menu Items](/l/pt/developers/extend/apps/layout/command-menu-items) |
|
||||
| Entidade | Finalidade | Documentação |
|
||||
| ----------------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| **Aplicação** | Identidade da aplicação, função padrão, variáveis | [Application Config](/l/pt/developers/extend/apps/config/application) |
|
||||
| **Papel** | Conjuntos de permissões para objetos e campos | [Roles & Permissions](/l/pt/developers/extend/apps/config/roles) |
|
||||
| **Objeto** | Tipos de registro personalizados com campos | [Objects](/l/pt/developers/extend/apps/data/objects) |
|
||||
| **Campo** | Adicionar campos a objetos de outros apps | [Extending Objects](/l/pt/developers/extend/apps/data/extending-objects) |
|
||||
| **Relação** | Links bidirecionais entre objetos | [Relations](/l/pt/developers/extend/apps/data/relations) |
|
||||
| **Função lógica** | TypeScript no lado do servidor com gatilhos | [Funções lógicas](/l/pt/developers/extend/apps/logic/logic-functions) |
|
||||
| **Habilidade** | Instruções reutilizáveis para agentes de IA | [Habilidades e Agentes](/l/pt/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agente** | Assistentes de IA com prompts personalizados | [Habilidades e Agentes](/l/pt/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Provedor de conexão** | Credenciais OAuth para APIs de terceiros | [Connections](/l/pt/developers/extend/apps/logic/connections) |
|
||||
| **Vista** | Vistas de lista de registros pré-configuradas | [Views](/l/pt/developers/extend/apps/layout/views) |
|
||||
| **Item do menu de navegação** | Entradas personalizadas na barra lateral | [Navigation Menu Items](/l/pt/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Layout da Página** | Abas e widgets na página de detalhes de um registro | [Page Layouts](/l/pt/developers/extend/apps/layout/page-layouts) |
|
||||
| **Componente de front-end** | UI React em sandbox dentro do Twenty | [Componentes de front-end](/l/pt/developers/extend/apps/layout/front-components) |
|
||||
| **Item do menu de comandos** | Ações rápidas e entradas Cmd+K | [Command Menu Items](/l/pt/developers/extend/apps/layout/command-menu-items) |
|
||||
|
||||
## Sandboxing
|
||||
|
||||
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
|
||||
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
|
||||
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
|
||||
* **Funções lógicas** são executadas em processos Node.js isolados no servidor. Elas acessam dados apenas por meio do cliente de API tipado, restrito às permissões do papel do aplicativo.
|
||||
* **Componentes de front-end** executam em Web Workers usando Remote DOM — isolados da página principal, mas renderizando elementos DOM nativos (não iframes). Eles se comunicam com o Twenty por meio de uma API de host com passagem de mensagens.
|
||||
* **Permissões** são aplicadas no nível da API. O token de tempo de execução (`TWENTY_APP_ACCESS_TOKEN`) é derivado do papel definido em `defineApplication()`.
|
||||
|
||||
## App lifecycle
|
||||
## Ciclo de vida do aplicativo
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
@@ -76,26 +76,26 @@ 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 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](/l/pt/developers/extend/apps/config/install-hooks) for details.
|
||||
* **`yarn twenty dev`** — observa seus arquivos-fonte e sincroniza ao vivo as alterações com um servidor Twenty conectado. O cliente de API tipado é regenerado automaticamente quando o esquema muda.
|
||||
* **`yarn twenty build`** — compila TypeScript, empacota funções de lógica e componentes de front-end com o esbuild e produz um manifesto.
|
||||
* **Hooks de pré/pós-instalação** — funções opcionais que são executadas durante a instalação. Veja [Install Hooks](/l/pt/developers/extend/apps/config/install-hooks) para detalhes.
|
||||
|
||||
## Next steps
|
||||
## Próximos passos
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/pt/developers/extend/apps/config/overview">
|
||||
Application identity, default role, and install hooks.
|
||||
<Card title="Configuração" icon="screwdriver-wrench" href="/l/pt/developers/extend/apps/config/overview">
|
||||
Identidade da aplicação, função padrão e hooks de instalação.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/pt/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
Objetos, campos e relações bidirecionais.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/pt/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Lógica" icon="bolt" href="/l/pt/developers/extend/apps/logic/overview">
|
||||
Funções de lógica, skills, agentes e conexões OAuth.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/pt/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Views, navegação, layouts de página, componentes de front.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/pt/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Operações" icon="rocket" href="/l/pt/developers/extend/apps/operations/overview">
|
||||
CLI, testes, remotes, CI e publicação do seu app.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
---
|
||||
title: Local Server
|
||||
description: Manage the local Twenty Docker server — start, stop, upgrade, parallel test instance, and manual SDK setup.
|
||||
title: Servidor Local
|
||||
description: Gerencie o servidor Twenty Docker local — inicie, pare, atualize, instância de teste em paralelo e configuração manual do SDK.
|
||||
icon: server
|
||||
---
|
||||
|
||||
## Managing the local server
|
||||
## Gerenciando o servidor local
|
||||
|
||||
Use `yarn twenty server` to control the local Twenty container:
|
||||
Use `yarn twenty server` para controlar o contêiner Twenty local:
|
||||
|
||||
| Command | What it does |
|
||||
| -------------------------------------- | -------------------------------------------- |
|
||||
| `yarn twenty server start` | Start the server (pulls the image if needed) |
|
||||
| `yarn twenty server start --port 3030` | Start on a custom port |
|
||||
| `yarn twenty server stop` | Stop the server (preserves data) |
|
||||
| `yarn twenty server status` | Show URL, version, and login credentials |
|
||||
| `yarn twenty server logs` | Stream server logs |
|
||||
| `yarn twenty server reset` | Wipe data and start fresh |
|
||||
| `yarn twenty server upgrade` | Pull the latest `twenty-app-dev` image |
|
||||
| `yarn twenty server upgrade 2.2.0` | Upgrade to a specific version |
|
||||
| Comando | O que faz |
|
||||
| -------------------------------------- | ------------------------------------------------ |
|
||||
| `yarn twenty server start` | Inicia o servidor (baixa a imagem se necessário) |
|
||||
| `yarn twenty server start --port 3030` | Iniciar em uma porta personalizada |
|
||||
| `yarn twenty server stop` | Interrompe o servidor (preserva os dados) |
|
||||
| `yarn twenty server status` | Mostra a URL, a versão e as credenciais de login |
|
||||
| `yarn twenty server logs` | Transmite os logs do servidor |
|
||||
| `yarn twenty server reset` | Apaga os dados e começa do zero |
|
||||
| `yarn twenty server upgrade` | Baixa a imagem mais recente `twenty-app-dev` |
|
||||
| `yarn twenty server upgrade 2.2.0` | Atualizar para uma versão específica |
|
||||
|
||||
Data persists across restarts in two Docker volumes (`twenty-app-dev-data` for PostgreSQL, `twenty-app-dev-storage` for files). Use `reset` to wipe everything.
|
||||
Os dados são persistidos entre reinicializações em dois volumes do Docker (`twenty-app-dev-data` para PostgreSQL, `twenty-app-dev-storage` para arquivos). Use `reset` para apagar tudo.
|
||||
|
||||
## Upgrading the server image
|
||||
## Atualizando a imagem do servidor
|
||||
|
||||
`yarn twenty server upgrade` pulls the latest image, compares digests, and only recreates the container if anything actually changed. Volumes are preserved — only the container is replaced. If a new image was pulled and the container was running, the upgrade automatically starts a new container; run `yarn twenty server start` afterward to wait for it to become healthy.
|
||||
`yarn twenty server upgrade` baixa a imagem mais recente, compara os digests e só recria o contêiner se algo realmente tiver mudado. Os volumes são preservados — apenas o contêiner é substituído. Se uma nova imagem foi baixada e o contêiner estava em execução, a atualização inicia automaticamente um novo contêiner; execute `yarn twenty server start` depois para aguardar até que ele fique saudável.
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty server upgrade # Latest
|
||||
yarn twenty server upgrade 2.2.0 # Specific version
|
||||
```
|
||||
|
||||
Verify the running version with `yarn twenty server status` (it shows the `APP_VERSION` baked into the container).
|
||||
Verifique a versão em execução com `yarn twenty server status` (ele mostra o `APP_VERSION` incorporado ao contêiner).
|
||||
|
||||
## Running a parallel test instance
|
||||
## Executando uma instância de teste paralela
|
||||
|
||||
Pass `--test` to any `server` command to manage a second, fully isolated instance — useful for integration tests or experiments without touching your main dev data:
|
||||
Passe `--test` para qualquer comando de `server` para gerenciar uma segunda instância totalmente isolada — útil para testes de integração ou para experimentar sem tocar nos seus dados principais de desenvolvimento:
|
||||
|
||||
| Command | What it does |
|
||||
| ----------------------------------- | ----------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Start the test instance (defaults to port 2021) |
|
||||
| `yarn twenty server stop --test` | Stop it |
|
||||
| `yarn twenty server status --test` | Show its status |
|
||||
| `yarn twenty server logs --test` | Stream its logs |
|
||||
| `yarn twenty server reset --test` | Wipe its data |
|
||||
| `yarn twenty server upgrade --test` | Upgrade its image |
|
||||
| Comando | O que faz |
|
||||
| ----------------------------------- | ------------------------------------------------ |
|
||||
| `yarn twenty server start --test` | Inicia a instância de teste (padrão: porta 2021) |
|
||||
| `yarn twenty server stop --test` | Parar |
|
||||
| `yarn twenty server status --test` | Mostrar seu status |
|
||||
| `yarn twenty server logs --test` | Transmitir seus logs |
|
||||
| `yarn twenty server reset --test` | Apagar seus dados |
|
||||
| `yarn twenty server upgrade --test` | Atualizar sua imagem |
|
||||
|
||||
The test instance has its own container (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`), and config — it runs alongside your main instance without conflicts. Combine `--test` with `--port` to override 2021.
|
||||
A instância de teste tem seu próprio contêiner (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) e configuração — ela é executada junto com sua instância principal sem conflitos. Combine `--test` com `--port` para substituir 2021.
|
||||
|
||||
## Manual setup (without the scaffolder)
|
||||
## Configuração manual (sem o gerador)
|
||||
|
||||
Skip the scaffolder if you're adding the SDK to an existing project:
|
||||
Ignore a ferramenta de scaffolding se você estiver adicionando o SDK a um projeto existente:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
```
|
||||
|
||||
Add the script to `package.json`:
|
||||
Adicione o script ao `package.json`:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -65,8 +65,8 @@ Add the script to `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
You can now run `yarn twenty dev`, `yarn twenty server start`, and the rest.
|
||||
Agora você pode executar `yarn twenty dev`, `yarn twenty server start` e o restante.
|
||||
|
||||
<Note>
|
||||
Don't install `twenty-sdk` globally — pin it per project so each app uses its own version.
|
||||
Não instale `twenty-sdk` globalmente — fixe-o por projeto, para que cada aplicativo use sua própria versão.
|
||||
</Note>
|
||||
|
||||
+12
-12
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: What's inside a scaffolded Twenty app — files, folders, and what each one does.
|
||||
title: Estrutura do projeto
|
||||
description: O que há dentro de um app Twenty criado com scaffold — arquivos, pastas e o que cada um faz.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
A new app generated by `npx create-twenty-app` looks like this:
|
||||
Um novo app gerado por `npx create-twenty-app` se parece com isto:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
@@ -25,16 +25,16 @@ my-twenty-app/
|
||||
README.md, LLMS.md
|
||||
```
|
||||
|
||||
## Key files
|
||||
## Arquivos principais
|
||||
|
||||
| File / Folder | Purpose |
|
||||
| ---------------------------------------- | -------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Required.** The main configuration file for your app. |
|
||||
| `src/default-role.ts` | Default role controlling what your logic functions can access. |
|
||||
| `src/constants/universal-identifiers.ts` | Auto-generated UUIDs and metadata (display name, description). |
|
||||
| `src/__tests__/` | Integration tests (setup + example test). |
|
||||
| `public/` | Static assets (images, fonts) served with your app. |
|
||||
| Arquivo / Pasta | Finalidade |
|
||||
| ---------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| `src/application-config.ts` | **Obrigatório.** O principal arquivo de configuração do seu aplicativo. |
|
||||
| `src/default-role.ts` | Papel padrão que controla o que suas funções de lógica podem acessar. |
|
||||
| `src/constants/universal-identifiers.ts` | UUIDs gerados automaticamente e metadados (nome de exibição, descrição). |
|
||||
| `src/__tests__/` | Testes de integração (configuração + teste de exemplo). |
|
||||
| `public/` | Recursos estáticos (imagens, fontes) servidos com seu aplicativo. |
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** The folders above are conventions — the SDK detects entities via AST analysis on `export default defineEntity(...)` calls regardless of where the file lives.
|
||||
**A organização de arquivos fica a seu critério.** As pastas acima são convenções — o SDK detecta entidades por meio de análise de AST em chamadas a `export default defineEntity(...)`, independentemente de onde o arquivo esteja.
|
||||
</Note>
|
||||
|
||||
@@ -1,184 +1,184 @@
|
||||
---
|
||||
title: Quick Start
|
||||
title: Início rápido
|
||||
icon: rocket
|
||||
description: Create your first Twenty app in minutes.
|
||||
description: Crie seu primeiro app do Twenty em minutos.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
## Pré-requisitos
|
||||
|
||||
* **Node.js 24+** — [Download](https://nodejs.org/)
|
||||
* **Yarn 4** — bundled with Node via Corepack. Enable it: `corepack enable`
|
||||
* **Docker** — [Download](https://www.docker.com/products/docker-desktop/). Needed to run a local Twenty server. Skip if you already have Twenty running elsewhere.
|
||||
* **Node.js 24+** — [Baixar](https://nodejs.org/)
|
||||
* **Yarn 4** — Vem com o Node.js via Corepack. Ative-o: `corepack enable`
|
||||
* **Docker** — [Baixar](https://www.docker.com/products/docker-desktop/). Necessário para executar um servidor Twenty local. Ignore se você já tiver o Twenty em execução em outro lugar.
|
||||
|
||||
Building a Twenty app has three phases. The scaffolder collapses them into one happy-path command, but each phase is a separate concept — when something fails, knowing which phase you're in tells you what to fix.
|
||||
A criação de um aplicativo Twenty tem três fases. A ferramenta de scaffolding as reúne em um único comando do fluxo ideal, mas cada fase é um conceito separado — quando algo falha, saber em que fase você está indica o que corrigir.
|
||||
|
||||
| Phase | What you do | Tool | Result |
|
||||
| ------------------- | ---------------------------------- | ----------------------------- | ----------------------------- |
|
||||
| **1. Scaffold** | Generate the app's source code | `npx create-twenty-app` | A TypeScript project on disk |
|
||||
| **2. Run a server** | Start a Twenty server to sync into | Docker + `yarn twenty server` | A running Twenty instance |
|
||||
| **3. Sync** | Live-sync your code to the server | `yarn twenty dev` | Your changes appear in the UI |
|
||||
| Fase | O que você faz | Ferramenta | Resultado |
|
||||
| --------------------------- | -------------------------------------------------- | ----------------------------- | ------------------------------------- |
|
||||
| **1. Criar scaffolding** | Gerar o código-fonte do aplicativo | `npx create-twenty-app` | Um projeto TypeScript em disco |
|
||||
| **2. Executar um servidor** | Iniciar um servidor Twenty para o qual sincronizar | Docker + `yarn twenty server` | Uma instância Twenty em execução |
|
||||
| **3. Sincronizar** | Sincronize seu código em tempo real com o servidor | `yarn twenty dev` | Suas alterações aparecem na interface |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Scaffold your project
|
||||
## Fase 1 — Fazer scaffolding do seu projeto
|
||||
|
||||
Create a new app from the template:
|
||||
Crie um novo aplicativo a partir do modelo:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
You'll be prompted for a name and description — press **Enter** for the defaults. This generates a TypeScript project in `my-twenty-app/` with a starter `application-config.ts`, a default role, a CI workflow, and an integration test.
|
||||
Você será solicitado a informar um nome e uma descrição — pressione **Enter** para aceitar os valores padrão. Isso gera um projeto TypeScript em `my-twenty-app/` com um `application-config.ts` inicial, um papel padrão, um fluxo de trabalho de CI e um teste de integração.
|
||||
|
||||
**After this phase:** you have an app's source code on your machine. It isn't running yet — that's Phase 2.
|
||||
**Após esta fase:** você tem o código-fonte de um aplicativo na sua máquina. Ele ainda não está em execução — isso é a Fase 2.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Run a local Twenty server
|
||||
## Fase 2 — Executar um servidor Twenty local
|
||||
|
||||
Your app needs a Twenty server to sync into. The server is a full Twenty instance — UI, GraphQL API, PostgreSQL — running locally in Docker. Your local code uploads its definitions to that server, which makes them appear in the UI.
|
||||
Seu aplicativo precisa de um servidor Twenty para o qual sincronizar. O servidor é uma instância completa do Twenty — interface, API GraphQL, PostgreSQL — executando localmente no Docker. Seu código local envia suas definições para esse servidor, o que faz com que elas apareçam na interface.
|
||||
|
||||
The scaffolder offers to start one for you:
|
||||
A ferramenta de scaffolding oferece iniciar um para você:
|
||||
|
||||
> **Would you like to set up a local Twenty instance?**
|
||||
> **Você gostaria de configurar uma instância local do Twenty?**
|
||||
|
||||
* **Yes (recommended)** — pulls the `twentycrm/twenty-app-dev` Docker image and starts it on port `2020`. Make sure Docker is running first.
|
||||
* **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote add`.
|
||||
* **Sim (recomendado)** — baixa a imagem Docker `twentycrm/twenty-app-dev` e a inicia na porta `2020`. Certifique-se de que o Docker esteja em execução primeiro.
|
||||
* **Não** — escolha isto se você já tiver um servidor Twenty ao qual deseja se conectar. Você pode conectá-lo depois com `yarn twenty remote add`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Deve iniciar instância local?" />
|
||||
</div>
|
||||
|
||||
Once the server is up, a browser opens for sign-in. Use the pre-seeded demo account:
|
||||
Quando o servidor estiver ativo, um navegador será aberto para login. Use a conta de demonstração pré-configurada:
|
||||
|
||||
* **Email:** `tim@apple.dev`
|
||||
* **Password:** `tim@apple.dev`
|
||||
* **E-mail:** `tim@apple.dev`
|
||||
* **Senha:** `tim@apple.dev`
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty login screen" />
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Tela de login do Twenty" />
|
||||
</div>
|
||||
|
||||
Click **Authorize** on the next screen — this gives the CLI access to your workspace.
|
||||
Clique em **Authorize** na próxima tela — isso dá à CLI acesso ao seu espaço de trabalho.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty CLI authorization screen" />
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Tela de autorização da CLI do Twenty" />
|
||||
</div>
|
||||
|
||||
Your terminal will confirm everything is set up.
|
||||
Seu terminal confirmará que tudo está configurado.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="App scaffolded successfully" />
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="Scaffold do aplicativo criado com sucesso" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a running Twenty server at [http://localhost:2020](http://localhost:2020) with your CLI authorized to sync to it.
|
||||
**Após esta fase:** você tem um servidor Twenty em execução em [http://localhost:2020](http://localhost:2020) com sua CLI autorizada a sincronizar com ele.
|
||||
|
||||
<Note>
|
||||
If Docker isn't installed or running, the scaffolder will tell you the right start command for your OS. Once Docker is up, you can resume with `yarn twenty server start` — no need to re-scaffold.
|
||||
Se o Docker não estiver instalado ou em execução, a ferramenta de scaffolding informará o comando de inicialização correto para o seu sistema operacional. Quando o Docker estiver ativo, você pode retomar com `yarn twenty server start` — sem necessidade de recriar o scaffolding.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Sync your changes
|
||||
## Fase 3 — Sincronizar suas alterações
|
||||
|
||||
This is the inner loop you'll spend most of your time in.
|
||||
Este é o ciclo interno no qual você passará a maior parte do tempo.
|
||||
|
||||
```bash filename="Terminal"
|
||||
cd my-twenty-app
|
||||
yarn twenty dev
|
||||
```
|
||||
|
||||
This watches `src/`, rebuilds on every change, and syncs the result to the server. Edit a file, save, and within a second the server reflects the change. You'll see a live status panel in your terminal.
|
||||
Isso monitora `src/`, recompila a cada alteração e sincroniza o resultado com o servidor. Edite um arquivo, salve e, em um segundo, o servidor refletirá a alteração. Você verá um painel de status em tempo real no seu terminal.
|
||||
|
||||
For more detailed output (build logs, sync requests, error traces), add `--verbose`.
|
||||
Para uma saída mais detalhada (logs de build, solicitações de sincronização, rastros de erro), adicione `--verbose`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Dev mode terminal output" />
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Saída do terminal no modo de desenvolvimento" />
|
||||
</div>
|
||||
|
||||
Open [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). You should see your app under **Your Apps**.
|
||||
Abra [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Você deverá ver seu aplicativo em **Your Apps**.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Your Apps list showing My twenty app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Lista Your Apps exibindo My twenty app" />
|
||||
</div>
|
||||
|
||||
Click **My twenty app** to see its **application registration** — a server-level record describing your app (name, identifier, OAuth credentials, source). One registration can be installed across multiple workspaces on the same server.
|
||||
Clique em **My twenty app** para ver seu **registro do aplicativo** — um registro em nível de servidor que descreve seu aplicativo (nome, identificador, credenciais OAuth, origem). Um registro pode ser instalado em vários espaços de trabalho no mesmo servidor.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Application registration details" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Detalhes do registro do aplicativo" />
|
||||
</div>
|
||||
|
||||
Click **View installed app** to see the workspace install. The **About** tab shows version and management options.
|
||||
Clique em **View installed app** para ver a instalação no espaço de trabalho. A aba **About** mostra a versão e as opções de gerenciamento.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Installed app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Aplicação instalada" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a live development loop. Edit any file in `src/` and it appears in the UI.
|
||||
**Após esta fase:** você tem um ciclo de desenvolvimento em tempo real. Edite qualquer arquivo em `src/` e ele aparecerá na interface.
|
||||
|
||||
### One-shot sync for CI and scripts
|
||||
### Sincronização única para CI e scripts
|
||||
|
||||
Pass `--once` to run a single build + sync and exit — same pipeline, no watcher:
|
||||
Passe `--once` para executar uma única compilação + sincronização e sair — mesmo pipeline, sem watcher:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once
|
||||
```
|
||||
|
||||
| Command | Behavior | When to use |
|
||||
| ------------------------ | ------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `yarn twenty dev` | Watches and re-syncs on every change. Runs until you stop it. | Interactive local development. |
|
||||
| `yarn twenty dev --once` | Single build + sync, exits `0` on success, `1` on failure. | CI, pre-commit hooks, AI agents, scripted workflows. |
|
||||
| Comando | Comportamento | Quando usar |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `yarn twenty dev` | Monitora e ressincroniza a cada alteração. Fica em execução até você interrompê-lo. | Desenvolvimento local interativo. |
|
||||
| `yarn twenty dev --once` | Executa uma única compilação + sincronização e, em seguida, encerra com o código `0` em caso de sucesso ou `1` em caso de falha. | Scripts, CI, hooks de pre-commit, agentes de IA e fluxos de trabalho com script. |
|
||||
|
||||
Both modes need a server in development mode and an authenticated remote.
|
||||
Ambos os modos precisam de um servidor em modo de desenvolvimento e de um remoto autenticado.
|
||||
|
||||
<Warning>
|
||||
Dev mode is only available on Twenty instances running in development (`NODE_ENV=development`). Production instances reject dev sync requests — use `yarn twenty deploy` to deploy to production servers. See [Publishing](/l/pt/developers/extend/apps/operations/publishing).
|
||||
O modo de desenvolvimento só está disponível em instâncias do Twenty em modo de desenvolvimento (`NODE_ENV=development`). Instâncias de produção rejeitam solicitações de sincronização de desenvolvimento — use `yarn twenty deploy` para implantar em servidores de produção. Veja [Publicação](/l/pt/developers/extend/apps/operations/publishing).
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Starting from an example
|
||||
## Começando a partir de um exemplo
|
||||
|
||||
Use `--example` to start with a more complete project (custom objects, fields, logic functions, front components):
|
||||
Use `--example` para começar com um projeto mais completo (objetos personalizados, campos, funções de lógica, componentes de front-end):
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app --example postcard
|
||||
```
|
||||
|
||||
Examples live in [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). You can also scaffold individual entities into an existing project with `yarn twenty add` — see [Scaffolding](/l/pt/developers/extend/apps/getting-started/scaffolding).
|
||||
Os exemplos estão em [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Você também pode criar o scaffolding de entidades individuais em um projeto existente com `yarn twenty add` — veja [Scaffolding](/l/pt/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
---
|
||||
|
||||
## What you can build
|
||||
## O que você pode criar
|
||||
|
||||
Apps are composed of **entities** — each defined as a TypeScript file with a single `export default`:
|
||||
Os aplicativos são compostos por **entidades** — cada uma definida como um arquivo TypeScript com um único `export default`:
|
||||
|
||||
| Entity | What it does |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------- |
|
||||
| **Objects & Fields** | Custom data models (Post Card, Invoice, etc.) with typed fields |
|
||||
| **Logic functions** | Server-side TypeScript triggered by HTTP routes, cron schedules, or database events |
|
||||
| **Front components** | React components that render inside Twenty's UI (side panel, widgets, command menu) |
|
||||
| **Skills & Agents** | AI capabilities — reusable instructions and autonomous assistants |
|
||||
| **Views & Navigation** | Pre-configured list views and sidebar menu items |
|
||||
| **Page layouts** | Custom record detail pages with tabs and widgets |
|
||||
| Entidade | O que faz |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Objetos e campos** | Modelos de dados personalizados (Cartão postal, Fatura etc.) com campos tipados |
|
||||
| **Funções lógicas** | Funções TypeScript do lado do servidor acionadas por rotas HTTP, agendamentos do cron ou eventos de banco de dados |
|
||||
| **Componentes de front-end** | Componentes React que são renderizados na UI do Twenty (painel lateral, widgets, menu de comandos) |
|
||||
| **Habilidades e agentes** | Recursos de IA — instruções reutilizáveis e assistentes autônomos |
|
||||
| **Exibições e navegação** | Exibições de lista pré-configuradas e itens de menu da barra lateral |
|
||||
| **Layouts de página** | Páginas de detalhes de registros personalizadas com abas e widgets |
|
||||
|
||||
Full reference: [Concepts](/l/pt/developers/extend/apps/getting-started/concepts).
|
||||
Referência completa: [Conceitos](/l/pt/developers/extend/apps/getting-started/concepts).
|
||||
|
||||
## Next steps
|
||||
## Próximos passos
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/pt/developers/extend/apps/config/overview">
|
||||
Application identity, default role, install hooks, public assets.
|
||||
<Card title="Configuração" icon="screwdriver-wrench" href="/l/pt/developers/extend/apps/config/overview">
|
||||
Identidade do aplicativo, função padrão, hooks de instalação, recursos públicos.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/pt/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
Objetos, campos e relações bidirecionais.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/pt/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Lógica" icon="bolt" href="/l/pt/developers/extend/apps/logic/overview">
|
||||
Funções de lógica, skills, agents e conexões OAuth.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/pt/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Views, navegação, layouts de página, front components.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/pt/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Operações" icon="rocket" href="/l/pt/developers/extend/apps/operations/overview">
|
||||
CLI, testes, remotes, CI e publicação do seu aplicativo.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
title: Scaffolding
|
||||
description: Generate entity files interactively with yarn twenty add — objects, fields, views, logic functions, and more.
|
||||
description: Gere arquivos de entidade de forma interativa com `yarn twenty add` — objetos, campos, visualizações, funções de lógica e mais.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Instead of creating entity files by hand, use the interactive scaffolder:
|
||||
Em vez de criar arquivos de entidade manualmente, use o scaffolder interativo:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add
|
||||
```
|
||||
|
||||
It prompts you to pick an entity type and walks you through the required fields, then writes a ready-to-use file with a stable `universalIdentifier` and the correct `defineEntity()` call.
|
||||
Ele solicita que você escolha um tipo de entidade e orienta você pelos campos obrigatórios, depois grava um arquivo pronto para uso com um `universalIdentifier` estável e a chamada correta de `defineEntity()`.
|
||||
|
||||
You can also pass the entity type directly to skip the first prompt:
|
||||
Você também pode passar o tipo de entidade diretamente para pular o primeiro prompt:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add object
|
||||
@@ -20,38 +20,38 @@ yarn twenty add logicFunction
|
||||
yarn twenty add frontComponent
|
||||
```
|
||||
|
||||
## Available entity types
|
||||
## Tipos de entidade disponíveis
|
||||
|
||||
| Entity type | Command | Generated file |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Object | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Field | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Logic function | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Front component | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Role | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Skill | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| View | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Navigation menu item | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Page layout | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
| Tipo de entidade | Comando | Arquivo gerado |
|
||||
| ------------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Objeto | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Campo | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Função lógica | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Componente de front-end | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Função | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Habilidade | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agente | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| Vista | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Item do menu de navegação | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Layout da página | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
|
||||
## What the scaffolder generates
|
||||
## O que o scaffolder gera
|
||||
|
||||
Each entity type has its own template. For example, `yarn twenty add object` asks for:
|
||||
Cada tipo de entidade tem seu próprio modelo. Por exemplo, `yarn twenty add object` solicita:
|
||||
|
||||
1. **Name (singular)** — e.g., `invoice`
|
||||
2. **Name (plural)** — e.g., `invoices`
|
||||
3. **Label (singular)** — auto-populated from the name (e.g., `Invoice`)
|
||||
4. **Label (plural)** — auto-populated (e.g., `Invoices`)
|
||||
5. **Create a view and navigation item?** — if you answer yes, the scaffolder also generates a matching view and sidebar link for the new object.
|
||||
1. **Nome (singular)** — por exemplo, `invoice`
|
||||
2. **Nome (plural)** — por exemplo, `invoices`
|
||||
3. **Rótulo (singular)** — preenchido automaticamente a partir do nome (por exemplo, `Invoice`)
|
||||
4. **Rótulo (plural)** — preenchido automaticamente (por exemplo, `Invoices`)
|
||||
5. **Criar uma view e um item de navegação?** — se você responder sim, o scaffolder também gera uma view correspondente e um link na barra lateral para o novo objeto.
|
||||
|
||||
Other entity types have simpler prompts — most only ask for a name.
|
||||
Outros tipos de entidade têm prompts mais simples — a maioria pede apenas um nome.
|
||||
|
||||
The `field` entity type is more detailed: it asks for the field name, label, type (from a list of all available field types like `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.), and the target object's `universalIdentifier`.
|
||||
O tipo de entidade `field` é mais detalhado: ele solicita o nome do campo, rótulo, tipo (a partir de uma lista de todos os tipos de campo disponíveis como `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.) e o `universalIdentifier` do objeto de destino.
|
||||
|
||||
## Custom output path
|
||||
## Caminho de saída personalizado
|
||||
|
||||
Use the `--path` flag to place the generated file in a custom location:
|
||||
Use a opção `--path` para colocar o arquivo gerado em um local personalizado:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add logicFunction --path src/custom-folder
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Command Menu Items
|
||||
description: Surface front components as quick actions and command menu (Cmd+K) entries with defineCommandMenuItem.
|
||||
title: Itens do menu de comandos
|
||||
description: Apresente front components como ações rápidas e entradas do menu de comandos (Cmd+K) com `defineCommandMenuItem`.
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
A **command menu item** is the bridge between the user and a [front component](/l/pt/developers/extend/apps/layout/front-components). It registers the component in Twenty's command menu (Cmd+K) and, optionally, as a pinned quick-action button in the top-right corner of the page.
|
||||
Um **item de menu de comando** é a ponte entre o usuário e um [front component](/l/pt/developers/extend/apps/layout/front-components). Ele registra o componente no menu de comandos (Cmd+K) do Twenty e, opcionalmente, como um botão fixado de ação rápida no canto superior direito da página.
|
||||
|
||||
```ts src/command-menu-items/open-dashboard.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -20,25 +20,25 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration fields
|
||||
## Campos de configuração
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Yes | Stable unique ID for the command |
|
||||
| `label` | Yes | Full label shown in the command menu (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Yes | The `universalIdentifier` of the front component this command opens |
|
||||
| `shortLabel` | No | Shorter label displayed on the pinned quick-action button |
|
||||
| `icon` | No | Icon name displayed next to the label (e.g. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | No | When `true`, shows the command as a quick-action button in the top-right corner of the page |
|
||||
| `availabilityType` | No | Controls where the command appears: `'GLOBAL'` (always available), `'RECORD_SELECTION'` (only when records are selected), or `'FALLBACK'` (shown when no other commands match) |
|
||||
| `availabilityObjectUniversalIdentifier` | No | Restrict the command to pages of a specific object type (e.g. only on Company records) |
|
||||
| `conditionalAvailabilityExpression` | No | A boolean expression that dynamically controls visibility (see below) |
|
||||
| Campo | Obrigatório | Descrição |
|
||||
| --------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Sim | ID exclusivo e estável para o comando |
|
||||
| `label` | Sim | Rótulo completo exibido no menu de comandos (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Sim | O `universalIdentifier` do componente de front-end que este comando abre |
|
||||
| `shortLabel` | Não | Rótulo mais curto exibido no botão fixado de ação rápida |
|
||||
| `icon` | Não | Nome do ícone exibido ao lado do rótulo (por exemplo, `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | Não | Quando `true`, mostra o comando como um botão de ação rápida no canto superior direito da página |
|
||||
| `availabilityType` | Não | Controla onde o comando aparece: `'GLOBAL'` (sempre disponível), `'RECORD_SELECTION'` (apenas quando registros estão selecionados) ou `'FALLBACK'` (exibido quando nenhum outro comando corresponde) |
|
||||
| `availabilityObjectUniversalIdentifier` | Não | Restringe o comando a páginas de um tipo específico de objeto (por exemplo, somente em registros de Company) |
|
||||
| `conditionalAvailabilityExpression` | Não | Uma expressão booleana que controla dinamicamente a visibilidade (veja abaixo) |
|
||||
|
||||
## Headless commands
|
||||
## Comandos sem interface
|
||||
|
||||
A command menu item paired with a [headless front component](/l/pt/developers/extend/apps/layout/front-components#headless-vs-non-headless) is the idiomatic way to ship a one-click action — run code, navigate, or confirm and execute. The Front Components page covers the [SDK Command components](/l/pt/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) that handle the action-and-unmount pattern.
|
||||
Um item de menu de comando emparelhado com um [headless front component](/l/pt/developers/extend/apps/layout/front-components#headless-vs-non-headless) é a forma idiomática de disponibilizar uma ação de um clique — executar código, navegar ou confirmar e executar. A página de Front Components aborda os [SDK Command components](/l/pt/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) que lidam com o padrão de ação e desmontagem.
|
||||
|
||||
A typical flow:
|
||||
Um fluxo típico:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -79,9 +79,9 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Conditional availability expressions
|
||||
## Expressões de disponibilidade condicional
|
||||
|
||||
The `conditionalAvailabilityExpression` field lets you control when a command is visible based on the current page context. Import typed variables and operators from `twenty-sdk` to build expressions:
|
||||
O campo `conditionalAvailabilityExpression` permite controlar quando um comando é visível com base no contexto da página atual. Importe variáveis tipadas e operadores de `twenty-sdk` para construir expressões:
|
||||
|
||||
```ts src/command-menu-items/bulk-update.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -103,42 +103,42 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
### Context variables
|
||||
### Variáveis de contexto
|
||||
|
||||
These represent the current state of the page:
|
||||
Elas representam o estado atual da página:
|
||||
|
||||
| Variable | Type | Description |
|
||||
| ------------------------------ | --------- | ---------------------------------------------------------------- |
|
||||
| `pageType` | `string` | Current page type (e.g. `'RecordIndexPage'`, `'RecordShowPage'`) |
|
||||
| `isInSidePanel` | `boolean` | Whether the component is rendered in a side panel |
|
||||
| `numberOfSelectedRecords` | `number` | Number of currently selected records |
|
||||
| `isSelectAll` | `boolean` | Whether "select all" is active |
|
||||
| `selectedRecords` | `array` | The selected record objects |
|
||||
| `favoriteRecordIds` | `array` | IDs of favorited records |
|
||||
| `objectPermissions` | `object` | Permissions for the current object type |
|
||||
| `targetObjectReadPermissions` | `object` | Read permissions for the target object |
|
||||
| `targetObjectWritePermissions` | `object` | Write permissions for the target object |
|
||||
| `featureFlags` | `object` | Active feature flags |
|
||||
| `objectMetadataItem` | `object` | Metadata of the current object type |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Whether the current view has a soft-delete filter |
|
||||
| Variável | Tipo | Descrição |
|
||||
| ------------------------------ | --------- | --------------------------------------------------------------------------- |
|
||||
| `pageType` | `string` | Tipo de página atual (por exemplo, `'RecordIndexPage'`, `'RecordShowPage'`) |
|
||||
| `isInSidePanel` | `boolean` | Se o componente é renderizado em um painel lateral |
|
||||
| `numberOfSelectedRecords` | `number` | Número de registros atualmente selecionados |
|
||||
| `isSelectAll` | `boolean` | Se "selecionar tudo" está ativo |
|
||||
| `selectedRecords` | `array` | Os objetos de registro selecionados |
|
||||
| `favoriteRecordIds` | `array` | IDs dos registros marcados como favoritos |
|
||||
| `objectPermissions` | `object` | Permissões para o tipo de objeto atual |
|
||||
| `targetObjectReadPermissions` | `object` | Permissões de leitura para o objeto alvo |
|
||||
| `targetObjectWritePermissions` | `object` | Permissões de escrita para o objeto alvo |
|
||||
| `featureFlags` | `object` | Flags de recurso ativas |
|
||||
| `objectMetadataItem` | `object` | Metadados do tipo de objeto atual |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Se a visualização atual tem um filtro de soft-delete |
|
||||
|
||||
### Operators
|
||||
### Operadores
|
||||
|
||||
Combine variables into boolean expressions:
|
||||
Combine variáveis em expressões booleanas:
|
||||
|
||||
| Operator | Description |
|
||||
| ----------------------------------- | ----------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true` if the value is not null/undefined |
|
||||
| `isNonEmptyString(value)` | `true` if the value is a non-empty string |
|
||||
| `includes(array, value)` | `true` if the array contains the value |
|
||||
| `includesEvery(array, prop, value)` | `true` if every item's property includes the value |
|
||||
| `every(array, prop)` | `true` if the property is truthy on every item |
|
||||
| `everyDefined(array, prop)` | `true` if the property is defined on every item |
|
||||
| `everyEquals(array, prop, value)` | `true` if the property equals the value on every item |
|
||||
| `some(array, prop)` | `true` if the property is truthy on at least one item |
|
||||
| `someDefined(array, prop)` | `true` if the property is defined on at least one item |
|
||||
| `someEquals(array, prop, value)` | `true` if the property equals the value on at least one item |
|
||||
| `someNonEmptyString(array, prop)` | `true` if the property is a non-empty string on at least one item |
|
||||
| `none(array, prop)` | `true` if the property is falsy on every item |
|
||||
| `noneDefined(array, prop)` | `true` if the property is undefined on every item |
|
||||
| `noneEquals(array, prop, value)` | `true` if the property does not equal the value on any item |
|
||||
| Operador | Descrição |
|
||||
| ----------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true` se o valor não for null/undefined |
|
||||
| `isNonEmptyString(value)` | `true` se o valor for uma string não vazia |
|
||||
| `includes(array, value)` | `true` se o array contiver o valor |
|
||||
| `includesEvery(array, prop, value)` | `true` se a propriedade de cada item incluir o valor |
|
||||
| `every(array, prop)` | `true` se a propriedade for truthy em cada item |
|
||||
| `everyDefined(array, prop)` | `true` se a propriedade estiver definida em cada item |
|
||||
| `everyEquals(array, prop, value)` | `true` se a propriedade for igual ao valor em cada item |
|
||||
| `some(array, prop)` | `true` se a propriedade for truthy em pelo menos um item |
|
||||
| `someDefined(array, prop)` | `true` se a propriedade estiver definida em pelo menos um item |
|
||||
| `someEquals(array, prop, value)` | `true` se a propriedade for igual ao valor em pelo menos um item |
|
||||
| `someNonEmptyString(array, prop)` | `true` se a propriedade for uma string não vazia em pelo menos um item |
|
||||
| `none(array, prop)` | `true` se a propriedade for falsy em cada item |
|
||||
| `noneDefined(array, prop)` | `true` se a propriedade for undefined em cada item |
|
||||
| `noneEquals(array, prop, value)` | `true` se a propriedade não for igual ao valor em nenhum item |
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
---
|
||||
title: Front Components
|
||||
description: Build React components that render inside Twenty's UI with sandboxed isolation.
|
||||
title: Componentes de front-end
|
||||
description: Crie componentes React que renderizam dentro da UI do Twenty com isolamento em sandbox.
|
||||
icon: window-maximize
|
||||
---
|
||||
|
||||
Front components are React components that render directly inside Twenty's UI. They run in an **isolated Web Worker** using Remote DOM — your code is sandboxed but renders natively in the page, not in an iframe.
|
||||
Componentes de front-end são componentes React que renderizam diretamente dentro da UI do Twenty. Eles são executados em um Web Worker isolado usando Remote DOM — seu código é sandboxed, mas renderiza nativamente na página, não em um iframe.
|
||||
|
||||
## Where front components can be used
|
||||
## Onde os componentes de front-end podem ser usados
|
||||
|
||||
Front components can render in two locations within Twenty:
|
||||
Os componentes de front-end podem ser renderizados em dois locais dentro do Twenty:
|
||||
|
||||
* **Side panel** — Non-headless front components open in the right-hand side panel. This is the default behavior when a front component is triggered from the command menu.
|
||||
* **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside [page layouts](/l/pt/developers/extend/apps/layout/page-layouts). When configuring a dashboard or a record page layout, users can add a front component widget.
|
||||
* **Painel lateral** — Componentes de front-end não headless abrem no painel lateral direito. Este é o comportamento padrão quando um componente de front-end é acionado pelo menu de comandos.
|
||||
* **Widgets (painéis e páginas de registro)** — Componentes de front-end podem ser incorporados como widgets dentro de [page layouts](/l/pt/developers/extend/apps/layout/page-layouts). Ao configurar um painel ou o layout de uma página de registro, os usuários podem adicionar um widget de componente de front-end.
|
||||
|
||||
A front component on its own isn't reachable from the UI — you need to *surface* it. The two ways to do that are:
|
||||
Um front component por si só não é acessível pela interface — é preciso *exibi-lo*. As duas maneiras de fazer isso são:
|
||||
|
||||
* **Pair it with a [command menu item](/l/pt/developers/extend/apps/layout/command-menu-items)** — registers it in the command menu (Cmd+K) and, optionally, as a pinned quick-action.
|
||||
* **Embed it as a widget in a [page layout](/l/pt/developers/extend/apps/layout/page-layouts)** — places it on a record's detail page or dashboard.
|
||||
* **Associe-o a um [command menu item](/l/pt/developers/extend/apps/layout/command-menu-items)** — registra-o no menu de comandos (Cmd+K) e, opcionalmente, como uma ação rápida fixada.
|
||||
* **Incorpore-o como um widget em um [page layout](/l/pt/developers/extend/apps/layout/page-layouts)** — posiciona-o na página de detalhes de um registro ou em um painel.
|
||||
|
||||
## Basic example
|
||||
## Exemplo básico
|
||||
|
||||
The quickest way to see a front component in action is to pair it with a [`defineCommandMenuItem`](/l/pt/developers/extend/apps/layout/command-menu-items), so it appears as a quick-action button in the top-right corner of the page:
|
||||
A maneira mais rápida de ver um front component em ação é associá-lo a um [`defineCommandMenuItem`](/l/pt/developers/extend/apps/layout/command-menu-items), para que ele apareça como um botão de ação rápida no canto superior direito da página:
|
||||
|
||||
```tsx src/front-components/hello-world.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -56,35 +56,35 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
After syncing with `yarn twenty dev` (or running a one-shot `yarn twenty dev --once`), the quick action appears in the top-right corner of the page:
|
||||
Após sincronizar com `yarn twenty dev` (ou executando uma única vez o `yarn twenty dev --once`), a ação rápida aparece no canto superior direito da página:
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Quick action button in the top-right corner" />
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Botão de ação rápida no canto superior direito" />
|
||||
</div>
|
||||
|
||||
Click it to render the component inline.
|
||||
Clique nele para renderizar o componente inline.
|
||||
|
||||
## Configuration fields
|
||||
## Campos de configuração
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------------- | -------- | ------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Yes | Stable unique ID for this component |
|
||||
| `component` | Yes | A React component function |
|
||||
| `name` | No | Display name |
|
||||
| `description` | No | Description of what the component does |
|
||||
| `isHeadless` | No | Set to `true` if the component has no visible UI (see below) |
|
||||
| Campo | Obrigatório | Descrição |
|
||||
| --------------------- | ----------- | ---------------------------------------------------------------------------- |
|
||||
| `universalIdentifier` | Sim | ID único e estável para este componente |
|
||||
| `component` | Sim | Uma função de componente React |
|
||||
| `name` | Não | Nome de Exibição |
|
||||
| `description` | Não | Descrição do que o componente faz |
|
||||
| `isHeadless` | Não | Defina como `true` se o componente não tiver interface visível (veja abaixo) |
|
||||
|
||||
## Placing a front component on a page
|
||||
## Colocando um componente de front-end em uma página
|
||||
|
||||
Beyond commands, you can embed a front component directly into a record page by adding it as a widget in a **page layout**. See [Page Layouts](/l/pt/developers/extend/apps/layout/page-layouts) for details.
|
||||
Além de comandos, você pode incorporar um componente de front-end diretamente em uma página de registro adicionando-o como um widget em um **layout de página**. Veja [Page Layouts](/l/pt/developers/extend/apps/layout/page-layouts) para mais detalhes.
|
||||
|
||||
## Headless vs non-headless
|
||||
## Headless vs não headless
|
||||
|
||||
Front components come in two rendering modes controlled by the `isHeadless` option:
|
||||
Os componentes de front-end têm dois modos de renderização controlados pela opção `isHeadless`:
|
||||
|
||||
**Non-headless (default)** — The component renders a visible UI. When triggered from the command menu it opens in the side panel. This is the default behavior when `isHeadless` is `false` or omitted.
|
||||
**Não headless (padrão)** — O componente renderiza uma interface visível. Quando acionado pelo menu de comandos, ele é aberto no painel lateral. Este é o comportamento padrão quando `isHeadless` é `false` ou omitido.
|
||||
|
||||
**Headless (`isHeadless: true`)** — The component mounts invisibly in the background. It does not open the side panel. Headless components are designed for actions that execute logic and then unmount themselves — for example, running an async task, navigating to a page, or showing a confirmation modal. They pair naturally with the SDK Command components described below.
|
||||
**Headless (`isHeadless: true`)** — O componente é montado de forma invisível em segundo plano. Ele não abre o painel lateral. Componentes headless são projetados para ações que executam lógica e, em seguida, se desmontam — por exemplo, executar uma tarefa assíncrona, navegar para uma página ou exibir um modal de confirmação. Eles se combinam naturalmente com os componentes Command do SDK descritos abaixo.
|
||||
|
||||
```tsx src/front-components/sync-tracker.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -110,20 +110,20 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
Because the component returns `null`, Twenty skips rendering a container for it — no empty space appears in the layout. The component still has access to all hooks and the host communication API.
|
||||
Como o componente retorna `null`, o Twenty ignora renderizar um contêiner para ele — nenhum espaço vazio aparece no layout. O componente ainda tem acesso a todos os hooks e à API de comunicação do host.
|
||||
|
||||
## SDK Command components
|
||||
## Componentes Command do SDK
|
||||
|
||||
The `twenty-sdk` package provides four Command helper components designed for headless front components. Each component executes an action on mount, handles errors by showing a snackbar notification, and automatically unmounts the front component when done.
|
||||
O pacote `twenty-sdk` fornece quatro componentes auxiliares Command projetados para componentes de front-end headless. Cada componente executa uma ação ao montar, trata erros exibindo uma notificação de snackbar e desmonta automaticamente o componente de front-end ao concluir.
|
||||
|
||||
Import them from `twenty-sdk/command`:
|
||||
Importe-os de `twenty-sdk/command`:
|
||||
|
||||
* **`Command`** — Runs an async callback via the `execute` prop.
|
||||
* **`CommandLink`** — Navigates to an app path. Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — Opens a confirmation modal. If the user confirms, executes the `execute` callback. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — Opens a specific side panel page. Props: `page`, `pageTitle`, `pageIcon`.
|
||||
* **`Command`** — Executa um callback assíncrono via a prop `execute`.
|
||||
* **`CommandLink`** — Navega para um caminho do app. Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — Abre um modal de confirmação. Se o usuário confirmar, executa o callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — Abre uma página específica do painel lateral. Props: `page`, `pageTitle`, `pageIcon`.
|
||||
|
||||
Here is a full example of a headless front component using `Command` to run an action from the command menu:
|
||||
Aqui está um exemplo completo de um componente de front-end headless usando `Command` para executar uma ação a partir do menu de comandos:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -165,7 +165,7 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
And an example using `CommandModal` to ask for confirmation before executing:
|
||||
E um exemplo usando `CommandModal` para solicitar confirmação antes de executar:
|
||||
|
||||
```tsx src/front-components/delete-draft.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -196,9 +196,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
## Accessing runtime context
|
||||
## Acessando o contexto de execução
|
||||
|
||||
Inside your component, use SDK hooks to access the current user, record, and component instance:
|
||||
Dentro do seu componente, use hooks do SDK para acessar o usuário atual, o registro e a instância do componente:
|
||||
|
||||
```tsx src/front-components/record-info.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -229,31 +229,31 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
Available hooks:
|
||||
Hooks disponíveis:
|
||||
|
||||
| Hook | Returns | Description |
|
||||
| --------------------------------------------- | ------------------ | ---------------------------------------------------------- |
|
||||
| `useUserId()` | `string` or `null` | The current user's ID |
|
||||
| `useSelectedRecordIds()` | `string[]` | All selected record IDs (empty array if none selected) |
|
||||
| `useRecordId()` | `string` or `null` | **Deprecated.** Use `useSelectedRecordIds()` instead |
|
||||
| `useFrontComponentId()` | `string` | This component instance's ID |
|
||||
| `useFrontComponentExecutionContext(selector)` | varies | Access the full execution context with a selector function |
|
||||
| Hook | Retorna | Descrição |
|
||||
| --------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------- |
|
||||
| `useUserId()` | `string` ou `null` | O ID do usuário atual |
|
||||
| `useSelectedRecordIds()` | `string[]` | Todos os IDs dos registros selecionados (array vazio se nenhum estiver selecionado) |
|
||||
| `useRecordId()` | `string` ou `null` | **Obsoleto.** Use `useSelectedRecordIds()` em vez disso |
|
||||
| `useFrontComponentId()` | `string` | O ID desta instância do componente |
|
||||
| `useFrontComponentExecutionContext(selector)` | varia | Acesse o contexto de execução completo com uma função seletora |
|
||||
|
||||
## Host communication API
|
||||
## API de comunicação do host
|
||||
|
||||
Front components can trigger navigation, modals, and notifications using functions from `twenty-sdk`:
|
||||
Componentes de front-end podem acionar navegação, modais e notificações usando funções de `twenty-sdk`:
|
||||
|
||||
| Function | Description |
|
||||
| ----------------------------------------------- | ----------------------------- |
|
||||
| `navigate(to, params?, queryParams?, options?)` | Navigate to a page in the app |
|
||||
| `openSidePanelPage(params)` | Open a side panel |
|
||||
| `closeSidePanel()` | Close the side panel |
|
||||
| `openCommandConfirmationModal(params)` | Show a confirmation dialog |
|
||||
| `enqueueSnackbar(params)` | Show a toast notification |
|
||||
| `unmountFrontComponent()` | Unmount the component |
|
||||
| `updateProgress(progress)` | Update a progress indicator |
|
||||
| Função | Descrição |
|
||||
| ----------------------------------------------- | ------------------------------------- |
|
||||
| `navigate(to, params?, queryParams?, options?)` | Navegar para uma página no app |
|
||||
| `openSidePanelPage(params)` | Abrir um painel lateral |
|
||||
| `closeSidePanel()` | Fechar o painel lateral |
|
||||
| `openCommandConfirmationModal(params)` | Mostrar um diálogo de confirmação |
|
||||
| `enqueueSnackbar(params)` | Mostrar uma notificação do tipo toast |
|
||||
| `unmountFrontComponent()` | Desmontar o componente |
|
||||
| `updateProgress(progress)` | Atualizar um indicador de progresso |
|
||||
|
||||
Here is an example that uses the host API to show a snackbar and close the side panel after an action completes:
|
||||
Aqui está um exemplo que usa a API do host para exibir um snackbar e fechar o painel lateral após a conclusão de uma ação:
|
||||
|
||||
```tsx src/front-components/archive-record.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -298,9 +298,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
### Working with multiple records
|
||||
### Trabalhando com vários registros
|
||||
|
||||
Use `useSelectedRecordIds()` to handle multiple selected records. This is useful for bulk operations:
|
||||
Use `useSelectedRecordIds()` para lidar com vários registros selecionados. Isso é útil para operações em lote:
|
||||
|
||||
```tsx src/front-components/bulk-export.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -353,9 +353,9 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
## Public assets
|
||||
## Recursos públicos
|
||||
|
||||
Front components can access files from the app's `public/` directory using `getPublicAssetUrl`:
|
||||
Componentes de front-end podem acessar arquivos do diretório `public/` do app usando `getPublicAssetUrl`:
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -369,18 +369,18 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
See the [public assets section](/l/pt/developers/extend/apps/config/public-assets) for details.
|
||||
Veja a [seção de recursos públicos](/l/pt/developers/extend/apps/config/public-assets) para obter detalhes.
|
||||
|
||||
## Styling
|
||||
## Estilização
|
||||
|
||||
Front components support multiple styling approaches. You can use:
|
||||
Componentes de front-end suportam várias abordagens de estilização. Você pode usar:
|
||||
|
||||
* **Inline styles** — `style={{ color: 'red' }}`
|
||||
* **Twenty UI components** — import from `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar, and more)
|
||||
* **Emotion** — CSS-in-JS with `@emotion/react`
|
||||
* **Styled-components** — `styled.div` patterns
|
||||
* **Tailwind CSS** — utility classes
|
||||
* **Any CSS-in-JS library** compatible with React
|
||||
* **Estilos inline** — `style={{ color: 'red' }}`
|
||||
* **Componentes de UI do Twenty** — importe de `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar e mais)
|
||||
* **Emotion** — CSS-in-JS com `@emotion/react`
|
||||
* **Styled-components** — padrões `styled.div`
|
||||
* **Tailwind CSS** — classes utilitárias
|
||||
* **Qualquer biblioteca CSS-in-JS** compatível com React
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Navigation Menu Items
|
||||
description: Add custom entries to the workspace sidebar — links to saved views or external URLs.
|
||||
title: Itens do menu de navegação
|
||||
description: Adicione entradas personalizadas à barra lateral do espaço de trabalho — links para visualizações salvas ou URLs externas.
|
||||
icon: bars
|
||||
---
|
||||
|
||||
A **navigation menu item** is an entry in the left sidebar. Use `defineNavigationMenuItem()` to ship custom sidebar links — typically one per [view](/l/pt/developers/extend/apps/layout/views) you ship — or to point at external URLs.
|
||||
Um **item do menu de navegação** é uma entrada na barra lateral esquerda. Use `defineNavigationMenuItem()` para disponibilizar links personalizados na barra lateral — normalmente um por [visualização](/l/pt/developers/extend/apps/layout/views) que você disponibiliza — ou para apontar para URLs externas.
|
||||
|
||||
```ts src/navigation-menu-items/example-navigation-menu-item.ts
|
||||
import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define';
|
||||
@@ -23,22 +23,22 @@ export default defineNavigationMenuItem({
|
||||
|
||||
## Pontos-chave
|
||||
|
||||
* `type` determines what the menu item links to. Each type pairs with a specific identifier field:
|
||||
* `type` determina para onde o item de menu aponta. Cada tipo é associado a um campo identificador específico:
|
||||
|
||||
| Tipo | O que faz | Required field |
|
||||
| ------------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | Opens a saved view | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Opens an external URL | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Groups nested items under a label | `name` (and child items reference the folder via `folderUniversalIdentifier`) |
|
||||
| `NavigationMenuItemType.OBJECT` | Opens an object's default index page | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Opens a standalone page layout | `pageLayoutUniversalIdentifier` |
|
||||
| Tipo | O que faz | Campo obrigatório |
|
||||
| ------------------------------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | Abre uma visualização salva | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Abre uma URL externa | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Agrupa itens aninhados sob um rótulo | `name` (e itens filhos fazem referência à pasta por meio de `folderUniversalIdentifier`) |
|
||||
| `NavigationMenuItemType.OBJECT` | Abre a página de índice padrão de um objeto | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Abre um layout de página independente | `pageLayoutUniversalIdentifier` |
|
||||
|
||||
* `position` controls ordering in the sidebar.
|
||||
* `position` controla a ordenação na barra lateral.
|
||||
|
||||
* `icon` and `color` are optional and customize how the entry looks.
|
||||
* `icon` e `color` são opcionais e personalizam a aparência da entrada.
|
||||
|
||||
* `folderUniversalIdentifier` is also available on any item to nest it inside a `FOLDER`-type parent.
|
||||
* `folderUniversalIdentifier` também está disponível em qualquer item para aninhá-lo dentro de um pai do tipo `FOLDER`.
|
||||
|
||||
<Note>
|
||||
**Common pitfall:** creating an object without an associated view + navigation menu item makes that object invisible to users. Unless it's a technical/internal object, every custom object should have a default view *and* a sidebar entry pointing at it.
|
||||
**Armadilha comum:** criar um objeto sem uma visualização associada + item do menu de navegação torna esse objeto invisível para os usuários. A menos que seja um objeto técnico/interno, todo objeto personalizado deve ter uma visualização padrão *e* uma entrada na barra lateral apontando para ela.
|
||||
</Note>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Place your app inside Twenty's UI — sidebar entries, saved views, record page tabs, and sandboxed React components.
|
||||
title: Visão Geral
|
||||
description: Coloque seu app dentro da interface do Twenty — entradas na barra lateral, visualizações salvas, abas na página de registro e componentes React em sandbox.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A Twenty app's **layout layer** is everything the user sees: where the app surfaces in the sidebar, which list views it ships, how its record detail pages are arranged, and which custom React components render inside those pages.
|
||||
A **camada de layout** de um app do Twenty é tudo o que o usuário vê: onde o app aparece na barra lateral, quais visualizações de lista ele fornece, como suas páginas de detalhes de registro são organizadas e quais componentes React personalizados são renderizados dentro dessas páginas.
|
||||
|
||||
```text
|
||||
Sidebar Record list Record detail page
|
||||
@@ -23,34 +23,34 @@ A Twenty app's **layout layer** is everything the user sees: where the app surfa
|
||||
and filters └─────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## Nesta seção
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Views" icon="list" href="/l/pt/developers/extend/apps/layout/views">
|
||||
`defineView` — saved list configurations: visible columns, filters, groups.
|
||||
<Card title="Visualizações" icon="lista" href="/l/pt/developers/extend/apps/layout/views">
|
||||
`defineView` — configurações salvas de lista: colunas visíveis, filtros, grupos.
|
||||
</Card>
|
||||
<Card title="Navigation Menu Items" icon="bars" href="/l/pt/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — sidebar entries pointing at views or external URLs.
|
||||
<Card title="Itens do menu de navegação" icon="bars" href="/l/pt/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — entradas da barra lateral apontando para visualizações ou URLs externas.
|
||||
</Card>
|
||||
<Card title="Page Layouts" icon="table-columns" href="/l/pt/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` and `definePageLayoutTab` — tabs and widgets on a record's detail page.
|
||||
<Card title="Layouts de Página" icon="table-columns" href="/l/pt/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` e `definePageLayoutTab` — abas e widgets na página de detalhes de um registro.
|
||||
</Card>
|
||||
<Card title="Front Components" icon="window-maximize" href="/l/pt/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — sandboxed React components that render inside Twenty.
|
||||
<Card title="Componentes de front-end" icon="window-maximize" href="/l/pt/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — componentes React em sandbox que são renderizados dentro do Twenty.
|
||||
</Card>
|
||||
<Card title="Command Menu Items" icon="terminal" href="/l/pt/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — register front components as Cmd+K entries and quick actions.
|
||||
<Card title="Itens do menu de comandos" icon="terminal" href="/l/pt/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — registra front components como entradas Cmd+K e ações rápidas.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Where the app surfaces
|
||||
## Onde o app aparece
|
||||
|
||||
| Surface | What it controls | Entity |
|
||||
| --------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Sidebar** | A custom entry linking to a saved view or external URL | `defineNavigationMenuItem` |
|
||||
| **Record list** | A saved configuration for an object — visible columns, order, filters, groups | `defineView` |
|
||||
| **Record detail page** | The tabs and widgets on a record page (your own object's, or a standard one) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **Inside any of the above** | A custom React widget — buttons, forms, dashboards, integrations | `defineFrontComponent` |
|
||||
| **Command menu (Cmd+K)** | A pinned quick action or hidden command | `defineCommandMenuItem` |
|
||||
| Superfície | O que controla | Entidade |
|
||||
| ------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Barra lateral** | Uma entrada personalizada que aponta para uma visualização salva ou URL externa | `defineNavigationMenuItem` |
|
||||
| **Lista de registros** | Uma configuração salva para uma lista de um objeto — colunas visíveis, ordem, filtros, grupos | `defineView` |
|
||||
| **Página de detalhes do registro** | As abas e widgets em uma página de registro (do seu próprio objeto ou de um padrão) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **Dentro de qualquer uma das opções acima** | Um widget React personalizado — botões, formulários, dashboards, integrações | `defineFrontComponent` |
|
||||
| **Menu de comandos (Cmd+K)** | Uma ação rápida fixada ou comando oculto | `defineCommandMenuItem` |
|
||||
|
||||
Front components run inside an isolated Web Worker using Remote DOM — they render *natively* in the page (not inside an iframe), but cannot reach the host page or DOM directly. Communication with Twenty happens through a message-passing host API.
|
||||
Os front components são executados dentro de um Web Worker isolado usando Remote DOM — eles são renderizados *nativamente* na página (não dentro de um iframe), mas não podem acessar diretamente a página host ou o DOM. A comunicação com o Twenty acontece por meio de uma API de host com passagem de mensagens.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Page Layouts
|
||||
description: Customize record detail pages — tabs, widgets, and where front components render — using definePageLayout and definePageLayoutTab.
|
||||
title: Layouts de Página
|
||||
description: Personalize páginas de detalhes de registros — abas, widgets e onde os front components são renderizados — usando `definePageLayout` e `definePageLayoutTab`.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A **page layout** controls how a record's detail page is arranged: which tabs appear and what widgets they contain. Use `definePageLayout()` to declare a layout for an object you own, or `definePageLayoutTab()` to add a single tab to a layout that already exists (yours or a standard Twenty one).
|
||||
Um **page layout** controla como a página de detalhes de um registro é organizada: quais abas aparecem e quais widgets elas contêm. Use `definePageLayout()` para declarar um layout para um objeto que você possui ou `definePageLayoutTab()` para adicionar uma única aba a um layout que já existe (seu ou um padrão da Twenty).
|
||||
|
||||
| Use case | Entity |
|
||||
| ---------------------------------------------------------------------- | --------------------- |
|
||||
| Define the entire layout for a record page on an object you own | `definePageLayout` |
|
||||
| Add one tab to an existing layout (your own object, or a standard one) | `definePageLayoutTab` |
|
||||
| Caso de uso | Entidade |
|
||||
| ------------------------------------------------------------------------------ | --------------------- |
|
||||
| Definir todo o layout para uma página de registro em um objeto que você possui | `definePageLayout` |
|
||||
| Adicionar uma aba a um layout existente (seu próprio objeto ou um padrão) | `definePageLayoutTab` |
|
||||
|
||||
## definePageLayout
|
||||
|
||||
Use this when you own the entire detail page — typically for a custom object you defined yourself.
|
||||
Use isto quando você possuir toda a página de detalhes — normalmente para um objeto personalizado que você próprio definiu.
|
||||
|
||||
```ts src/page-layouts/example-record-page-layout.ts
|
||||
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
|
||||
@@ -49,17 +49,17 @@ export default definePageLayout({
|
||||
});
|
||||
```
|
||||
|
||||
### Key points
|
||||
### Pontos-chave
|
||||
|
||||
* `type` is typically `'RECORD_PAGE'` to customize the detail view of a specific object.
|
||||
* `objectUniversalIdentifier` specifies which object this layout applies to.
|
||||
* Each `tab` defines a section of the page with a `title`, `position`, and `layoutMode` (`CANVAS` for free-form layout).
|
||||
* Each `widget` inside a tab can render a [front component](/l/pt/developers/extend/apps/layout/front-components), a relation list, or other built-in widget types.
|
||||
* `position` on tabs controls their order. Use higher values (e.g., 50) to place custom tabs after built-in ones.
|
||||
* `type` geralmente é `'RECORD_PAGE'` para personalizar a visualização de detalhes de um objeto específico.
|
||||
* `objectUniversalIdentifier` especifica a qual objeto este layout se aplica.
|
||||
* Cada `tab` define uma seção da página com um `title`, `position` e `layoutMode` (`CANVAS` para layout livre).
|
||||
* Cada `widget` dentro de uma aba pode renderizar um [front component](/l/pt/developers/extend/apps/layout/front-components), uma lista de relações ou outros tipos de widget nativos.
|
||||
* `position` nas abas controla sua ordem. Use valores mais altos (por exemplo, 50) para colocar abas personalizadas após as nativas.
|
||||
|
||||
## definePageLayoutTab
|
||||
|
||||
Use this when you only want to **add** a tab to an existing layout — for example, an analytics tab on the standard Company page, or an AI summary tab attached to your own object's layout.
|
||||
Use isto quando você quiser apenas **adicionar** uma aba a um layout existente — por exemplo, uma aba de analytics na página padrão de Company ou uma aba de resumo de IA anexada ao layout do seu próprio objeto.
|
||||
|
||||
```ts src/page-layouts/example-extra-tab.ts
|
||||
import {
|
||||
@@ -94,9 +94,9 @@ export default definePageLayoutTab({
|
||||
});
|
||||
```
|
||||
|
||||
### Key points
|
||||
### Pontos-chave
|
||||
|
||||
* `pageLayoutUniversalIdentifier` is **required** and must point to a page layout that already exists at install time — either a standard Twenty layout or one defined by your own app. Cross-app references to layouts owned by another installed app are not supported today. When the parent layout is missing, installation fails with a clear validation error.
|
||||
* `widgets` are scoped to this tab only — they reference [front components](/l/pt/developers/extend/apps/layout/front-components), views, etc. exactly like widgets defined inline in `definePageLayout`.
|
||||
* `position` controls ordering against existing tabs on the targeted layout. Pick a value that places your tab where you want it relative to built-in tabs.
|
||||
* Use this instead of `definePageLayout` when you only want to add to an existing layout. Use `definePageLayout` when you own the entire layout.
|
||||
* `pageLayoutUniversalIdentifier` é **obrigatório** e deve apontar para um page layout que já exista no momento da instalação — seja um layout padrão da Twenty ou um definido pelo seu próprio aplicativo. Referências entre apps para layouts pertencentes a outro aplicativo instalado não são compatíveis atualmente. Quando o layout pai estiver ausente, a instalação falha com um erro de validação claro.
|
||||
* `widgets` têm escopo apenas para esta aba — eles referenciam [front components](/l/pt/developers/extend/apps/layout/front-components), views etc., exatamente como widgets definidos inline em `definePageLayout`.
|
||||
* `position` controla a ordenação em relação às abas existentes no layout de destino. Escolha um valor que posicione sua aba onde você deseja em relação às abas nativas.
|
||||
* Use isto em vez de `definePageLayout` quando você quiser apenas adicionar a um layout existente. Use `definePageLayout` quando você possuir todo o layout.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Views
|
||||
description: Ship pre-configured saved views — column order, filters, groups — for objects in your app.
|
||||
title: Visualizações
|
||||
description: Envie visualizações salvas pré-configuradas — ordem das colunas, filtros, grupos — para objetos no seu app.
|
||||
icon: list
|
||||
---
|
||||
|
||||
A **view** is a saved configuration for how records of an object are displayed: which fields appear, their order, whether they're visible, and any filters or groups applied. Use `defineView()` to ship pre-configured views with your app — typically a default index view for each custom object you create.
|
||||
Uma **visualização** é uma configuração salva de como os registros de um objeto são exibidos: quais campos aparecem, sua ordem, se estão visíveis e quaisquer filtros ou grupos aplicados. Use `defineView()` para enviar visualizações pré-configuradas com o seu app — normalmente uma visualização de índice padrão para cada objeto personalizado que você cria.
|
||||
|
||||
```ts src/views/example-view.ts
|
||||
import { defineView, ViewKey } from 'twenty-sdk/define';
|
||||
@@ -30,14 +30,14 @@ export default defineView({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Pontos-chave
|
||||
|
||||
* `objectUniversalIdentifier` specifies which object this view applies to. It can be a custom object you defined or a standard Twenty object.
|
||||
* `key` determines the view type — `ViewKey.INDEX` is the main list view for the object.
|
||||
* `fields` controls which columns appear and in what order. Each field references a `fieldMetadataUniversalIdentifier`.
|
||||
* You can also declare `filters`, `filterGroups`, `groups`, and `fieldGroups` for advanced configurations.
|
||||
* `position` controls ordering when multiple views exist for the same object.
|
||||
* `objectUniversalIdentifier` especifica a qual objeto esta visualização se aplica. Pode ser um objeto personalizado que você definiu ou um objeto padrão do Twenty.
|
||||
* `key` determina o tipo de visualização — `ViewKey.INDEX` é a visualização de lista principal do objeto.
|
||||
* `fields` controla quais colunas aparecem e em que ordem. Cada campo referencia um `fieldMetadataUniversalIdentifier`.
|
||||
* Você também pode declarar `filters`, `filterGroups`, `groups` e `fieldGroups` para configurações avançadas.
|
||||
* `position` controla a ordenação quando existem várias visualizações para o mesmo objeto.
|
||||
|
||||
## How views show up in the UI
|
||||
## Como as visualizações aparecem na UI
|
||||
|
||||
A view by itself isn't reachable from the sidebar. To make it appear there, pair it with a [navigation menu item](/l/pt/developers/extend/apps/layout/navigation-menu-items) of type `VIEW` that points at the view's `universalIdentifier`. That's the canonical pattern: every custom object typically ships a default view + a sidebar entry that opens it.
|
||||
Uma visualização por si só não é acessível a partir da barra lateral. Para fazê-la aparecer lá, associe-a a um [item de menu de navegação](/l/pt/developers/extend/apps/layout/navigation-menu-items) do tipo `VIEW` que aponte para o `universalIdentifier` da visualização. Esse é o padrão canônico: cada objeto personalizado normalmente envia uma visualização padrão + uma entrada na barra lateral que a abre.
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
title: Connections
|
||||
description: Let your app act on a user's behalf in third-party services via OAuth.
|
||||
title: Conexões
|
||||
description: Permita que seu aplicativo aja em nome de um usuário em serviços de terceiros via OAuth.
|
||||
icon: plug
|
||||
---
|
||||
|
||||
Connections are credentials a user holds for an external service (Linear, GitHub, Slack, ...). Your app declares **how** those credentials are obtained — a **connection provider** — and consumes them at runtime to make authenticated calls to the third-party API.
|
||||
Conexões são credenciais que um usuário mantém para um serviço externo (Linear, GitHub, Slack, ...). Seu app declara **como** essas credenciais são obtidas — um **provedor de conexão** — e as consome em tempo de execução para fazer chamadas autenticadas à API de terceiros.
|
||||
|
||||
Today only OAuth 2.0 is supported. Future credential types (personal access tokens, API keys, basic auth) will plug into the same surface — apps already using `defineConnectionProvider({ type: 'oauth', ... })` won't need to migrate.
|
||||
Atualmente, apenas o OAuth 2.0 tem suporte. Tipos de credenciais futuros (tokens de acesso pessoal, chaves de API, autenticação básica) serão conectados à mesma interface — apps que já usam `defineConnectionProvider({ type: 'oauth', ... })` não precisarão migrar.
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="defineConnectionProvider" description="Declare how your app's connections are obtained">
|
||||
<Accordion title="defineConnectionProvider" description="Declare como as conexões do seu app são obtidas">
|
||||
|
||||
A connection provider describes the OAuth handshake your app needs. The user clicks "Add connection" in your app's settings, completes the provider's consent screen, and a `ConnectedAccount` row is created in their workspace.
|
||||
Um provedor de conexão descreve o handshake OAuth de que seu app precisa. O usuário clica em "Adicionar conexão" nas configurações do seu app, conclui a tela de consentimento do provedor e uma linha `ConnectedAccount` é criada no seu workspace.
|
||||
|
||||
A working setup needs **two files** — the connection provider, and a matching `serverVariables` declaration on `defineApplication` that holds the OAuth client credentials.
|
||||
Uma configuração funcional precisa de **dois arquivos** — o provedor de conexão e uma declaração correspondente de `serverVariables` em `defineApplication` que contém as credenciais do cliente OAuth.
|
||||
|
||||
```ts src/connection-providers/linear-connection.ts
|
||||
import { defineConnectionProvider } from 'twenty-sdk/define';
|
||||
@@ -71,16 +71,16 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Pontos-chave:
|
||||
|
||||
* `name` is the unique identifier string used in `listConnections({ providerName })` (kebab-case, must match `^[a-z][a-z0-9-]*$`).
|
||||
* `displayName` shows in the per-app settings tab and in the AI tool list.
|
||||
* `clientIdVariable` / `clientSecretVariable` are **names**, not values — they must match keys declared in `defineApplication.serverVariables`. The actual `client_id` and `client_secret` are entered by the server admin through the app registration UI, never committed to your repo.
|
||||
* Use `serverVariables` (not `applicationVariables`) — OAuth credentials are server-wide and one OAuth app per Twenty server.
|
||||
* Until both `serverVariables` are filled in, the per-app settings tab shows a "needs server admin" hint and the "Add connection" button is disabled.
|
||||
* `type: 'oauth'` is the only supported value today. The discriminator is forward-compatible: future types (`'pat'`, `'api-key'`, ...) will add new sub-config blocks alongside `oauth`.
|
||||
* `name` é a string de identificador exclusivo usada em `listConnections({ providerName })` (kebab-case, deve corresponder a `^[a-z][a-z0-9-]*$`).
|
||||
* `displayName` aparece na aba de configurações do app e na lista de ferramentas de IA.
|
||||
* `clientIdVariable` / `clientSecretVariable` são **nomes**, não valores — devem corresponder às chaves declaradas em `defineApplication.serverVariables`. Os `client_id` e `client_secret` reais são inseridos pelo administrador do servidor por meio da interface de registro do app e nunca são versionados no seu repositório.
|
||||
* Use `serverVariables` (não `applicationVariables`) — as credenciais OAuth são do servidor como um todo e há um app OAuth por servidor do Twenty.
|
||||
* Até que ambos os `serverVariables` sejam preenchidos, a aba de configurações do app mostra uma dica "precisa de administrador do servidor" e o botão "Adicionar conexão" fica desativado.
|
||||
* `type: 'oauth'` é o único valor compatível atualmente. O discriminador é compatível com versões futuras: tipos futuros (`'pat'`, `'api-key'`, ...) adicionarão novos blocos de subconfiguração ao lado de `oauth`.
|
||||
|
||||
The OAuth callback URL your provider needs to whitelist is:
|
||||
O URL de callback do OAuth que seu provedor precisa adicionar à lista de permissões é:
|
||||
|
||||
```
|
||||
https://<your-twenty-server>/apps/oauth/callback
|
||||
@@ -88,9 +88,9 @@ https://<your-twenty-server>/apps/oauth/callback
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="listConnections / getConnection" description="Use connections from a logic function">
|
||||
<Accordion title="listConnections / getConnection" description="Use conexões a partir de uma função de lógica">
|
||||
|
||||
Inside a logic function handler, `listConnections({ providerName })` returns this app's `ConnectedAccount` rows for the given provider, with refreshed access tokens.
|
||||
Dentro de um handler de função de lógica, `listConnections({ providerName })` retorna as linhas `ConnectedAccount` deste app para o provedor fornecido, com tokens de acesso atualizados.
|
||||
|
||||
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
||||
import { listConnections } from 'twenty-sdk/logic-function';
|
||||
@@ -135,34 +135,34 @@ export const createLinearIssueHandler = async (input: {
|
||||
};
|
||||
```
|
||||
|
||||
Each connection has:
|
||||
Cada conexão tem:
|
||||
|
||||
| Field | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Unique row id; pass to `getConnection(id)` to refetch a single one |
|
||||
| `visibility` | `'user'` (private to one workspace member) or `'workspace'` (shared with all members) |
|
||||
| `scopes` | OAuth permissions granted by the upstream provider (distinct from `visibility` — those are unrelated) |
|
||||
| `userWorkspaceId` | The owner's userWorkspace id — useful for picking "the request user's connection" in HTTP-route triggers |
|
||||
| `accessToken` | Fresh OAuth access token (refreshed automatically if expired) |
|
||||
| `name` / `handle` | The connection's display name (auto-derived at OAuth callback, user-renameable) |
|
||||
| `authFailedAt` | Set when the most recent refresh failed; the user must reconnect |
|
||||
| Campo | Descrição |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | ID de linha exclusivo; passe para `getConnection(id)` para buscar novamente um único registro |
|
||||
| `visibility` | `'user'` (privada para um membro do workspace) ou `'workspace'` (compartilhada com todos os membros) |
|
||||
| `scopes` | Permissões OAuth concedidas pelo provedor de origem (distintas de `visibility` — não têm relação) |
|
||||
| `userWorkspaceId` | O id de userWorkspace do proprietário — útil para selecionar "a conexão do usuário da requisição" em gatilhos de rota HTTP |
|
||||
| `accessToken` | Token de acesso OAuth recente (atualizado automaticamente se estiver expirado) |
|
||||
| `name` / `handle` | O nome de exibição da conexão (derivado automaticamente no callback do OAuth, renomeável pelo usuário) |
|
||||
| `authFailedAt` | Definido quando a atualização mais recente falhou; o usuário deve reconectar |
|
||||
|
||||
Key points:
|
||||
Pontos-chave:
|
||||
|
||||
* Pass `{ providerName }` to filter by provider; omit it to get all connections this app owns across all providers.
|
||||
* The server transparently refreshes the access token before returning. Your handler always sees a usable token (or `authFailedAt` set).
|
||||
* `getConnection(id)` is the single-row equivalent.
|
||||
* Passe `{ providerName }` para filtrar por provedor; omita para obter todas as conexões que este app possui em todos os provedores.
|
||||
* O servidor atualiza transparentemente o token de acesso antes de retornar. Seu handler sempre vê um token utilizável (ou `authFailedAt` definido).
|
||||
* `getConnection(id)` é o equivalente de uma única linha.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Per-user vs workspace-shared visibility" description="How users choose between private and shared credentials">
|
||||
<Accordion title="Visibilidade por usuário vs. compartilhada no workspace" description="Como os usuários escolhem entre credenciais privadas e compartilhadas">
|
||||
|
||||
When a user clicks "Add connection," they're prompted to pick a visibility:
|
||||
Quando um usuário clica em "Adicionar conexão", é solicitado que escolha uma visibilidade:
|
||||
|
||||
* **Just for me** — the credential is private to the connecting user. Any logic function called on their behalf (HTTP-route trigger with `isAuthRequired: true`) sees it; cron triggers and database events do not.
|
||||
* **Workspace shared** — any workspace member can use the credential. Cron / database triggers also see it, since they have no request user.
|
||||
* **Apenas para mim** — a credencial é privada para o usuário que a conectou. Qualquer função de lógica chamada em seu nome (gatilho de rota HTTP com `isAuthRequired: true`) a vê; gatilhos cron e eventos de banco de dados não.
|
||||
* **Compartilhada no workspace** — qualquer membro do workspace pode usar a credencial. Gatilhos de cron / banco de dados também a veem, pois não há um usuário da requisição.
|
||||
|
||||
Use the right one for each handler:
|
||||
Use a adequada para cada handler:
|
||||
|
||||
```ts
|
||||
// HTTP-route trigger — prefer the request user's own connection.
|
||||
@@ -174,19 +174,19 @@ const conn =
|
||||
const conn = connections.find((c) => c.visibility === 'workspace');
|
||||
```
|
||||
|
||||
Multiple connections per (user, provider) are allowed, so the same user can hold "Personal Linear" and "Work Linear" side by side.
|
||||
Várias conexões por (usuário, provedor) são permitidas, então o mesmo usuário pode manter "Linear pessoal" e "Linear de trabalho" lado a lado.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="One-time provider setup" description="Register your OAuth app with the third-party service">
|
||||
<Accordion title="Configuração única do provedor" description="Registre seu app OAuth no serviço de terceiros">
|
||||
|
||||
For each connection provider, the server admin needs to register an OAuth app at the third party first.
|
||||
Para cada provedor de conexão, o administrador do servidor precisa primeiro registrar um app OAuth no serviço de terceiros.
|
||||
|
||||
1. Go to the provider's developer settings (e.g. https://linear.app/settings/api/applications/new).
|
||||
2. Set the **Redirect URI** to `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Copy the generated **Client ID** and **Client Secret**.
|
||||
4. Open the installed app in Twenty as a server admin → set the values on the corresponding `serverVariables`.
|
||||
5. Workspace members can then add connections from the per-app **Connections** section.
|
||||
1. Acesse as configurações de desenvolvedor do provedor (por exemplo, https://linear.app/settings/api/applications/new).
|
||||
2. Defina a **URI de redirecionamento** como `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Copie o **ID do cliente** e o **Segredo do cliente** gerados.
|
||||
4. Abra o app instalado no Twenty como administrador do servidor → defina os valores nos `serverVariables` correspondentes.
|
||||
5. Os membros do workspace podem então adicionar conexões na seção **Conexões** de cada app.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Logic Functions
|
||||
description: Define server-side TypeScript functions with HTTP, cron, and database event triggers.
|
||||
title: Funções lógicas
|
||||
description: Defina funções TypeScript no lado do servidor com gatilhos HTTP, cron e de eventos de banco de dados.
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
Logic functions are server-side TypeScript functions that run on the Twenty platform. They can be triggered by HTTP requests, cron schedules, or database events — and can also be exposed as tools for AI agents.
|
||||
As funções de lógica são funções TypeScript no lado do servidor que são executadas na plataforma Twenty. Elas podem ser acionadas por solicitações HTTP, agendamentos cron ou eventos de banco de dados — e também podem ser expostas como ferramentas para agentes de IA.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineLogicFunction" description="Define logic functions and their triggers">
|
||||
<Accordion title="defineLogicFunction" description="Defina funções de lógica e seus gatilhos">
|
||||
|
||||
Each function file uses `defineLogicFunction()` to export a configuration with a handler and optional triggers.
|
||||
Cada arquivo de função usa `defineLogicFunction()` para exportar uma configuração com um manipulador e gatilhos opcionais.
|
||||
|
||||
```ts src/logic-functions/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -50,15 +50,15 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
Available trigger types:
|
||||
* **httpRoute**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
|
||||
> e.g. `path: '/post-card/create'` is callable at `https://your-twenty-server.com/s/post-card/create`
|
||||
* **cron**: Runs your function on a schedule using a CRON expression.
|
||||
* **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
|
||||
> e.g. `person.updated`, `*.created`, `company.*`
|
||||
Tipos de gatilho disponíveis:
|
||||
* **httpRoute**: Expõe sua função em um caminho e método HTTP **no endpoint `/s/`**:
|
||||
> por exemplo, `path: '/post-card/create'` é acessível em `https://your-twenty-server.com/s/post-card/create`
|
||||
* **cron**: Executa sua função em um agendamento usando uma expressão CRON.
|
||||
* **databaseEvent**: Executa em eventos do ciclo de vida de objetos do espaço de trabalho. Quando a operação do evento é `updated`, campos específicos a serem observados podem ser especificados no array `updatedFields`. Se deixar indefinido ou vazio, qualquer atualização acionará a função.
|
||||
> por exemplo, `person.updated`, `*.created`, `company.*`
|
||||
|
||||
<Note>
|
||||
You can also manually execute a function using the CLI:
|
||||
Você também pode executar manualmente uma função usando a CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
|
||||
@@ -68,18 +68,17 @@ yarn twenty exec -n create-new-post-card -p '{"key": "value"}'
|
||||
yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
You can watch logs with:
|
||||
Você pode acompanhar os logs com:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty logs
|
||||
```
|
||||
</Note>
|
||||
|
||||
#### Route trigger payload
|
||||
#### Payload de gatilho de rota
|
||||
|
||||
When a route trigger invokes your logic function, it receives a `RoutePayload` object that follows the
|
||||
[AWS HTTP API v2 format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html).
|
||||
Import the `RoutePayload` type from `twenty-sdk`:
|
||||
Quando um gatilho de rota invoca sua função de lógica, ela recebe um objeto `RoutePayload` que segue o [formato HTTP API v2 da AWS](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html).
|
||||
Importe o tipo `RoutePayload` de `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';
|
||||
@@ -92,24 +91,24 @@ const handler = async (event: RoutePayload) => {
|
||||
};
|
||||
```
|
||||
|
||||
The `RoutePayload` type has the following structure:
|
||||
O tipo `RoutePayload` tem a seguinte estrutura:
|
||||
|
||||
| Property | Type | Description | Example |
|
||||
| ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `headers` | `Record\<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) | see section below |
|
||||
| `queryStringParameters` | `Record\<string, string \| undefined>` | Query string parameters (multiple values joined with commas) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` |
|
||||
| `pathParameters` | `Record\<string, string \| undefined>` | Path parameters extracted from the route pattern | `/users/:id`, `/users/123` -> `{ id: '123' }` |
|
||||
| `body` | `object \| null` | Parsed request body (JSON) | `{ id: 1 }` -> `{ id: 1 }` |
|
||||
| `rawBody` | `string \| undefined` | Original UTF-8 request body, before JSON parsing. Useful for verifying HMAC-style webhook signatures (e.g. GitHub's `X-Hub-Signature-256`, Stripe). `undefined` when the runtime did not preserve it. | |
|
||||
| `isBase64Encoded` | `boolean` | Whether the body is base64 encoded | |
|
||||
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) | |
|
||||
| `requestContext.http.path` | `string` | Raw request path | |
|
||||
| Propriedade | Tipo | Descrição | Exemplo |
|
||||
| ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
|
||||
| `headers` | `Record\<string, string \| undefined>` | Cabeçalhos HTTP (apenas aqueles listados em `forwardedRequestHeaders`) | veja a seção abaixo |
|
||||
| `queryStringParameters` | `Record\<string, string \| undefined>` | Parâmetros de query string (valores múltiplos unidos por vírgulas) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` |
|
||||
| `pathParameters` | `Record\<string, string \| undefined>` | Parâmetros de caminho extraídos do padrão de rota | `/users/:id`, `/users/123` -> `{ id: '123' }` |
|
||||
| `body` | `object \| null` | Corpo da requisição analisado (JSON) | `{ id: 1 }` -> `{ id: 1 }` |
|
||||
| `rawBody` | `string \| undefined` | Corpo da requisição UTF-8 original, antes da análise de JSON. Útil para verificar assinaturas de webhook no estilo HMAC (por exemplo, `X-Hub-Signature-256` do GitHub, Stripe). `undefined` quando o ambiente de execução não o preservou. | |
|
||||
| `isBase64Encoded` | `boolean` | Se o corpo está codificado em base64 | |
|
||||
| `requestContext.http.method` | `string` | Método HTTP (GET, POST, PUT, PATCH, DELETE) | |
|
||||
| `requestContext.http.path` | `string` | Caminho bruto da requisição | |
|
||||
|
||||
|
||||
#### forwardedRequestHeaders
|
||||
|
||||
By default, HTTP headers from incoming requests are **not** passed to your logic function for security reasons.
|
||||
To access specific headers, list them in the `forwardedRequestHeaders` array:
|
||||
Por padrão, os cabeçalhos HTTP das requisições recebidas **não** são repassados para sua função de lógica por motivos de segurança.
|
||||
Para acessar cabeçalhos específicos, liste-os explicitamente no array `forwardedRequestHeaders`:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
@@ -125,7 +124,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
In your handler, access the forwarded headers like this:
|
||||
No seu manipulador, acesse os cabeçalhos encaminhados assim:
|
||||
|
||||
```ts
|
||||
const handler = async (event: RoutePayload) => {
|
||||
@@ -138,17 +137,17 @@ const handler = async (event: RoutePayload) => {
|
||||
```
|
||||
|
||||
<Note>
|
||||
Header names are normalized to lowercase. Access them using lowercase keys (e.g., `event.headers['content-type']`).
|
||||
Os nomes dos cabeçalhos são normalizados para minúsculas. Acesse-os usando chaves em minúsculas (por exemplo, `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
#### Exposing a function as an AI tool or workflow action
|
||||
#### Expor uma função como ferramenta de IA ou como ação de fluxo de trabalho
|
||||
|
||||
Logic functions can be exposed on two surfaces, each with its own trigger:
|
||||
As funções de lógica podem ser expostas em duas superfícies, cada uma com seu próprio gatilho:
|
||||
|
||||
* **`toolTriggerSettings`** — makes the function discoverable by Twenty's AI features (chat, MCP, function calling). Uses standard JSON Schema, the format LLMs natively understand.
|
||||
* **`workflowActionTriggerSettings`** — makes the function appear as a step in the visual workflow builder. Uses Twenty's rich `InputSchema` so the builder can render proper field editors, variable pickers, and labels.
|
||||
* **`toolTriggerSettings`** — torna a função disponível para os recursos de IA do Twenty (chat, MCP, chamadas de função). Usa o JSON Schema padrão, o formato que os LLMs entendem nativamente.
|
||||
* **`workflowActionTriggerSettings`** — torna a função visível como uma etapa no construtor visual de fluxos de trabalho. Usa o `InputSchema` avançado do Twenty para que o construtor possa renderizar editores de campo adequados, seletores de variáveis e rótulos.
|
||||
|
||||
A function can opt into one, the other, or both. They sit alongside `cronTriggerSettings`, `databaseEventTriggerSettings`, and `httpRouteTriggerSettings` — same pattern, same shape.
|
||||
Uma função pode optar por uma, pela outra ou por ambas. Ficam ao lado de `cronTriggerSettings`, `databaseEventTriggerSettings` e `httpRouteTriggerSettings` — mesmo padrão, mesmo formato.
|
||||
|
||||
```ts src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -182,10 +181,10 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Pontos-chave:
|
||||
|
||||
* A function can mix surfaces — declare both `toolTriggerSettings` and `workflowActionTriggerSettings` to expose it in chat AND in the workflow builder.
|
||||
* `toolTriggerSettings.inputSchema` and `workflowActionTriggerSettings.inputSchema` are both optional. When omitted, the manifest builder infers them from the handler source code (JSON Schema for the AI tool, Twenty's `InputSchema` for the workflow action). Provide one explicitly when you want richer typing — for example, with `FieldMetadataType`-aware fields like `CURRENCY` or `RELATION` for the workflow builder, or with `description` fields the AI agent can read:
|
||||
* Uma função pode misturar superfícies — declare tanto `toolTriggerSettings` quanto `workflowActionTriggerSettings` para expô-la no chat E no construtor de fluxos de trabalho.
|
||||
* `toolTriggerSettings.inputSchema` e `workflowActionTriggerSettings.inputSchema` são opcionais. Quando omitidos, o construtor de manifestos os infere a partir do código-fonte do handler (JSON Schema para a ferramenta de IA, `InputSchema` do Twenty para a ação de fluxo de trabalho). Forneça um explicitamente quando quiser uma tipagem mais rica — por exemplo, com campos compatíveis com `FieldMetadataType`, como `CURRENCY` ou `RELATION` para o construtor de fluxos de trabalho, ou com campos `description` que o agente de IA pode ler:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
@@ -210,29 +209,29 @@ export default defineLogicFunction({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Write a good `description`.** AI agents rely on the function's `description` field to decide when to use the tool. Be specific about what the tool does and when it should be called.
|
||||
**Escreva uma boa `description`.** Os agentes de IA dependem do campo `description` da função para decidir quando usar a ferramenta. Seja específico sobre o que a ferramenta faz e quando ela deve ser chamada.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<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](/l/pt/developers/extend/apps/config/install-hooks) for `definePreInstallLogicFunction` and `definePostInstallLogicFunction`.
|
||||
**Hooks de instalação** — manipuladores de pré-instalação e pós-instalação — compartilham esse ambiente de execução, mas são declarados com suas próprias funções de definição e não usam configurações de gatilho. Veja [Hooks de instalação](/l/pt/developers/extend/apps/config/install-hooks) para `definePreInstallLogicFunction` e `definePostInstallLogicFunction`.
|
||||
</Note>
|
||||
|
||||
## Typed API clients (twenty-client-sdk)
|
||||
## Clientes de API tipados (twenty-client-sdk)
|
||||
|
||||
The `twenty-client-sdk` package provides two typed GraphQL clients for interacting with the Twenty API from your logic functions and front components.
|
||||
O pacote `twenty-client-sdk` fornece dois clientes GraphQL tipados para interagir com a API do Twenty a partir das suas funções de lógica e componentes de front-end.
|
||||
|
||||
| Client | Import | Endpoint | Generated? |
|
||||
| ------------------- | ---------------------------- | ---------------------------------------------- | ---------------------- |
|
||||
| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — workspace data (records, objects) | Yes, at dev/build time |
|
||||
| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — workspace config, file uploads | No, ships pre-built |
|
||||
| Cliente | Importar | Endpoint | Gerado? |
|
||||
| ------------------- | ---------------------------- | -------------------------------------------------------------------- | -------------------------- |
|
||||
| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — dados do espaço de trabalho (registros, objetos) | Sim, em tempo de dev/build |
|
||||
| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — configuração do espaço de trabalho, upload de arquivos | Não, vem pré-compilado |
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="CoreApiClient" description="Query and mutate workspace data (records, objects)">
|
||||
<Accordion title="CoreApiClient" description="Consultar e modificar dados do espaço de trabalho (registros, objetos)">
|
||||
|
||||
`CoreApiClient` is the main client for querying and mutating workspace data. It is **generated from your workspace schema** during `yarn twenty dev` or `yarn twenty build`, so it is fully typed to match your objects and fields.
|
||||
`CoreApiClient` é o cliente principal para consultar e mutar dados do espaço de trabalho. Ele é **gerado a partir do schema do seu espaço de trabalho** durante `yarn twenty dev` ou `yarn twenty build`, então é totalmente tipado para corresponder aos seus objetos e campos.
|
||||
|
||||
```ts
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
@@ -269,15 +268,15 @@ const { createCompany } = await client.mutation({
|
||||
});
|
||||
```
|
||||
|
||||
The client uses a selection-set syntax: pass `true` to include a field, use `__args` for arguments, and nest objects for relations. You get full autocompletion and type checking based on your workspace schema.
|
||||
O cliente usa uma sintaxe de selection-set: passe `true` para incluir um campo, use `__args` para argumentos e aninhe objetos para relações. Você tem preenchimento automático e verificação de tipos completos com base no schema do seu espaço de trabalho.
|
||||
|
||||
<Note>
|
||||
**CoreApiClient is generated at dev/build time.** If you use it without running `yarn twenty dev` or `yarn twenty build` first, it throws an error. The generation happens automatically — the CLI introspects your workspace's GraphQL schema and generates a typed client using `@genql/cli`.
|
||||
**CoreApiClient é gerado em tempo de dev/build.** Se você usá-lo sem executar primeiro `yarn twenty dev` ou `yarn twenty build`, ele lançará um erro. A geração ocorre automaticamente — a CLI analisa o schema GraphQL do seu espaço de trabalho e gera um cliente tipado usando `@genql/cli`.
|
||||
</Note>
|
||||
|
||||
#### Using CoreSchema for type annotations
|
||||
#### Usando CoreSchema para anotações de tipo
|
||||
|
||||
`CoreSchema` provides TypeScript types matching your workspace objects — useful for typing component state or function parameters:
|
||||
`CoreSchema` fornece tipos TypeScript que correspondem aos objetos do seu espaço de trabalho — útil para tipar o estado de componentes ou parâmetros de função:
|
||||
|
||||
```ts
|
||||
import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core';
|
||||
@@ -299,9 +298,9 @@ setCompany(result.company);
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="MetadataApiClient" description="Workspace config, applications, and file uploads">
|
||||
<Accordion title="MetadataApiClient" description="Configuração do espaço de trabalho, aplicativos e upload de arquivos">
|
||||
|
||||
`MetadataApiClient` ships pre-built with the SDK (no generation required). It queries the `/metadata` endpoint for workspace configuration, applications, and file uploads.
|
||||
`MetadataApiClient` é fornecido pré-compilado com o SDK (não é necessário gerar). Ele consulta o endpoint `/metadata` para configuração do espaço de trabalho, aplicativos e upload de arquivos.
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
@@ -328,9 +327,9 @@ const { objects } = await metadataClient.query({
|
||||
});
|
||||
```
|
||||
|
||||
#### Uploading files
|
||||
#### Carregamento de arquivos
|
||||
|
||||
`MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields:
|
||||
`MetadataApiClient` inclui um método `uploadFile` para anexar arquivos a campos do tipo arquivo:
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
@@ -351,25 +350,25 @@ console.log(uploadedFile);
|
||||
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ---------------------------------- | -------- | --------------------------------------------------------------- |
|
||||
| `fileBuffer` | `Buffer` | The raw file contents |
|
||||
| `filename` | `string` | The name of the file (used for storage and display) |
|
||||
| `contentType` | `string` | MIME type (defaults to `application/octet-stream` if omitted) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | The `universalIdentifier` of the file-type field on your object |
|
||||
| Parâmetro | Tipo | Descrição |
|
||||
| ---------------------------------- | -------- | ----------------------------------------------------------------- |
|
||||
| `fileBuffer` | `Buffer` | O conteúdo bruto do arquivo |
|
||||
| `filename` | `string` | O nome do arquivo (usado para armazenamento e exibição) |
|
||||
| `contentType` | `string` | Tipo MIME (padrão para `application/octet-stream` se omitido) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | O `universalIdentifier` do campo do tipo de arquivo no seu objeto |
|
||||
|
||||
Key points:
|
||||
* Uses the field's `universalIdentifier` (not its workspace-specific ID), so your upload code works across any workspace where your app is installed.
|
||||
* The returned `url` is a signed URL you can use to access the uploaded file.
|
||||
Pontos-chave:
|
||||
* Usa o `universalIdentifier` do campo (não o ID específico do espaço de trabalho), de modo que seu código de upload funcione em qualquer espaço de trabalho onde seu aplicativo esteja instalado.
|
||||
* A `url` retornada é uma URL assinada que você pode usar para acessar o arquivo enviado.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
When your code runs on Twenty (logic functions or front components), the platform injects credentials as environment variables:
|
||||
Quando seu código é executado no Twenty (funções de lógica ou componentes de front-end), a plataforma injeta credenciais como variáveis de ambiente:
|
||||
|
||||
* `TWENTY_API_URL` — Base URL of the Twenty API
|
||||
* `TWENTY_APP_ACCESS_TOKEN` — Short-lived key scoped to your application's default function role
|
||||
* `TWENTY_API_URL` — URL base da API do Twenty
|
||||
* `TWENTY_APP_ACCESS_TOKEN` — Chave de curta duração com escopo para o papel de função padrão do seu aplicativo
|
||||
|
||||
You do **not** need to pass these to the clients — they read from `process.env` automatically. The API key's permissions are determined by the role referenced in `defaultRoleUniversalIdentifier` in your `application-config.ts`.
|
||||
Você **não** precisa passá-las para os clientes — eles leem de `process.env` automaticamente. As permissões da chave de API são determinadas pelo papel referenciado em `defaultRoleUniversalIdentifier` no seu `application-config.ts`.
|
||||
</Note>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Server-side TypeScript that runs inside Twenty — triggered by HTTP routes, cron schedules, database events, AI tools, or workflow actions.
|
||||
title: Visão Geral
|
||||
description: TypeScript do lado do servidor que é executado dentro do Twenty — acionado por rotas HTTP, agendamentos cron, eventos de banco de dados, ferramentas de IA ou ações de fluxos de trabalho.
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
A Twenty app's **logic layer** is the code that *runs* — server-side TypeScript handlers reacting to HTTP requests, cron schedules, and record changes; AI skills and agents that live inside the workspace; and OAuth connections that let your functions act on a user's behalf in third-party services.
|
||||
A **camada de lógica** de um app Twenty é o código que *é executado* — manipuladores TypeScript do lado do servidor reagindo a solicitações HTTP, agendamentos cron e alterações de registros; habilidades e agentes de IA que vivem dentro do workspace; e conexões OAuth que permitem que suas funções ajam em nome de um usuário em serviços de terceiros.
|
||||
|
||||
```text
|
||||
┌─ HTTP route ──┐
|
||||
@@ -22,34 +22,34 @@ A Twenty app's **logic layer** is the code that *runs* — server-side TypeScrip
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## Nesta seção
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Logic Functions" icon="bolt" href="/l/pt/developers/extend/apps/logic/logic-functions">
|
||||
The core building block — trigger types, payloads, and the typed API client.
|
||||
<Card title="Funções lógicas" icon="bolt" href="/l/pt/developers/extend/apps/logic/logic-functions">
|
||||
O bloco de construção principal — tipos de gatilho, payloads e o cliente de API tipado.
|
||||
</Card>
|
||||
<Card title="Skills & Agents" icon="robot" href="/l/pt/developers/extend/apps/logic/skills-and-agents">
|
||||
Reusable AI agent instructions and assistants with custom system prompts.
|
||||
<Card title="Habilidades e agentes" icon="robot" href="/l/pt/developers/extend/apps/logic/skills-and-agents">
|
||||
Instruções reutilizáveis para agentes de IA e assistentes com prompts de sistema personalizados.
|
||||
</Card>
|
||||
<Card title="Connections" icon="plug" href="/l/pt/developers/extend/apps/logic/connections">
|
||||
OAuth credentials your app holds for third-party services — Linear, GitHub, Slack, and more.
|
||||
<Card title="Conexões" icon="plug" href="/l/pt/developers/extend/apps/logic/connections">
|
||||
Credenciais OAuth que seu app mantém para serviços de terceiros — Linear, GitHub, Slack e outros.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Trigger types at a glance
|
||||
## Tipos de gatilho em resumo
|
||||
|
||||
A logic function picks one or more triggers — every entry below is a separate field on `defineLogicFunction()`:
|
||||
Uma função de lógica escolhe um ou mais gatilhos — cada entrada abaixo é um campo separado em `defineLogicFunction()`:
|
||||
|
||||
| Trigger | When it runs | Setting |
|
||||
| ------------------- | ---------------------------------------------------------- | ------------------------------- |
|
||||
| **HTTP route** | A request hits your `/s/\<path>` endpoint | `httpRouteTriggerSettings` |
|
||||
| **Cron** | A CRON expression matches | `cronTriggerSettings` |
|
||||
| **Database event** | A workspace record is created, updated, or deleted | `databaseEventTriggerSettings` |
|
||||
| **AI tool** | A Twenty AI feature decides to call your function | `toolTriggerSettings` |
|
||||
| **Workflow action** | A workflow step invokes your function | `workflowActionTriggerSettings` |
|
||||
| Disparador | Quando é executado | Configuração |
|
||||
| ----------------------------- | ----------------------------------------------------------------- | ------------------------------- |
|
||||
| **Rota HTTP** | Uma solicitação atinge seu endpoint `/s/\<path>` | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Uma expressão CRON corresponde | `cronTriggerSettings` |
|
||||
| **Evento de banco de dados** | Um registro do workspace é criado, atualizado ou excluído | `databaseEventTriggerSettings` |
|
||||
| **Ferramenta de IA** | Um recurso de IA do Twenty decide chamar sua função | `toolTriggerSettings` |
|
||||
| **Ação de fluxo de trabalho** | Uma etapa de fluxo de trabalho invoca sua função | `workflowActionTriggerSettings` |
|
||||
|
||||
Functions run sandboxed in isolated Node.js processes and access the workspace through a typed API client scoped to the role declared on [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
|
||||
As funções são executadas em sandbox em processos Node.js isolados e acessam o workspace por meio de um cliente de API tipado com escopo para a função declarada em [`defineApplication()`](/l/pt/developers/extend/apps/config/application).
|
||||
|
||||
<Note>
|
||||
**Install-time hooks** — code that runs before or after the install — share this runtime but use their own define functions and live under [Config → Install Hooks](/l/pt/developers/extend/apps/config/install-hooks).
|
||||
**Ganchos de instalação** — código que é executado antes ou depois da instalação — compartilham esse runtime, mas usam suas próprias funções define e ficam em [Config → Install Hooks](/l/pt/developers/extend/apps/config/install-hooks).
|
||||
</Note>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Skills & Agents
|
||||
description: Define AI skills and agents for your app.
|
||||
title: Habilidades e agentes
|
||||
description: Defina habilidades e agentes de IA para o seu aplicativo.
|
||||
icon: robot
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Skills and agents are currently in alpha. The feature works but is still evolving.
|
||||
As habilidades e os agentes estão atualmente em testes alfa. O recurso é funcional, mas ainda está evoluindo.
|
||||
</Warning>
|
||||
|
||||
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
|
||||
Os aplicativos podem definir capacidades de IA que residem dentro do espaço de trabalho — instruções de habilidades reutilizáveis e agentes com prompts de sistema personalizados.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineSkill" description="Define AI agent skills">
|
||||
<Accordion title="defineSkill" description="Defina habilidades de agentes de IA">
|
||||
|
||||
Skills define reusable instructions and capabilities that AI agents can use within your workspace. Use `defineSkill()` to define skills with built-in validation:
|
||||
As habilidades definem instruções e capacidades reutilizáveis que os agentes de IA podem usar no seu espaço de trabalho. Use `defineSkill()` para definir habilidades com validação integrada:
|
||||
|
||||
```ts src/skills/example-skill.ts
|
||||
import { defineSkill } from 'twenty-sdk/define';
|
||||
@@ -32,17 +32,17 @@ export default defineSkill({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is a unique identifier string for the skill (kebab-case recommended).
|
||||
* `label` is the human-readable display name shown in the UI.
|
||||
* `content` contains the skill instructions — this is the text the AI agent uses.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `description` (optional) provides additional context about the skill's purpose.
|
||||
Pontos-chave:
|
||||
* `name` é uma string de identificador exclusivo para a habilidade (recomenda-se kebab-case).
|
||||
* `label` é o nome de exibição legível por humanos mostrado na UI.
|
||||
* `content` contém as instruções da habilidade — este é o texto que o agente de IA usa.
|
||||
* `icon` (opcional) define o ícone exibido na UI.
|
||||
* `description` (opcional) fornece contexto adicional sobre a finalidade da habilidade.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="defineAgent" description="Define AI agents with custom prompts">
|
||||
<Accordion title="defineAgent" description="Defina agentes de IA com prompts personalizados">
|
||||
|
||||
Agents are AI assistants that live inside your workspace. Use `defineAgent()` to create agents with a custom system prompt:
|
||||
Os agentes são assistentes de IA que vivem dentro do seu espaço de trabalho. Use `defineAgent()` para criar agentes com um prompt de sistema personalizado:
|
||||
|
||||
```ts src/agents/example-agent.ts
|
||||
import { defineAgent } from 'twenty-sdk/define';
|
||||
@@ -57,13 +57,13 @@ export default defineAgent({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is the unique identifier string for the agent (kebab-case recommended).
|
||||
* `label` is the display name shown in the UI.
|
||||
* `prompt` is the system prompt that defines the agent's behavior.
|
||||
* `description` (optional) provides context about what the agent does.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `modelId` (optional) overrides the default AI model used by the agent.
|
||||
Pontos-chave:
|
||||
* `name` é a string de identificador exclusiva do agente (recomenda-se kebab-case).
|
||||
* `label` é o nome de exibição mostrado na UI.
|
||||
* `prompt` é o prompt do sistema que define o comportamento do agente.
|
||||
* `description` (opcional) fornece contexto sobre o que o agente faz.
|
||||
* `icon` (opcional) define o ícone exibido na UI.
|
||||
* `modelId` (opcional) substitui o modelo de IA padrão usado pelo agente.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: CLI
|
||||
description: yarn twenty commands for executing functions, streaming logs, managing app installations, and switching remotes.
|
||||
description: comandos `yarn twenty` para executar funções, transmitir logs, gerenciar instalações de aplicativos e alternar remotos.
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
Beyond `dev`, `build`, `add`, and `typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
|
||||
Além de `dev`, `build`, `add` e `typecheck`, a CLI `yarn twenty` fornece comandos para executar funções, visualizar logs e gerenciar instalações de aplicativos.
|
||||
|
||||
## Executing functions (`yarn twenty exec`)
|
||||
## Executando funções (`yarn twenty exec`)
|
||||
|
||||
Run a logic function manually without triggering it via HTTP, cron, or database event:
|
||||
Execute manualmente uma função de lógica sem acioná-la via HTTP, cron ou evento de banco de dados:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Execute by function name
|
||||
@@ -24,9 +24,9 @@ yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
## Viewing function logs (`yarn twenty logs`)
|
||||
## Visualizando logs de funções (`yarn twenty logs`)
|
||||
|
||||
Stream execution logs for your app's logic functions:
|
||||
Transmita os logs de execução das funções de lógica do seu aplicativo:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Stream all function logs
|
||||
@@ -40,12 +40,12 @@ yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is different from `yarn twenty server logs`, which shows the Docker container logs. `yarn twenty logs` shows your app's function execution logs from the Twenty server.
|
||||
Isso é diferente de `yarn twenty server logs`, que mostra os logs do contêiner Docker. `yarn twenty logs` mostra os logs de execução de funções do seu aplicativo a partir do servidor Twenty.
|
||||
</Note>
|
||||
|
||||
## Uninstalling an app (`yarn twenty uninstall`)
|
||||
## Desinstalando um aplicativo (`yarn twenty uninstall`)
|
||||
|
||||
Remove your app from the active workspace:
|
||||
Remova seu aplicativo do espaço de trabalho ativo:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty uninstall
|
||||
@@ -54,9 +54,9 @@ yarn twenty uninstall
|
||||
yarn twenty uninstall --yes
|
||||
```
|
||||
|
||||
## Managing remotes
|
||||
## Gerenciando remotos
|
||||
|
||||
A **remote** is a Twenty server that your app connects to. During setup, the scaffolder creates one for you automatically. You can add more remotes or switch between them at any time.
|
||||
Um **remoto** é um servidor Twenty ao qual seu aplicativo se conecta. Durante a configuração, o gerador de scaffold cria um para você automaticamente. Você pode adicionar mais remotos ou alternar entre eles a qualquer momento.
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new remote (opens a browser for OAuth login)
|
||||
@@ -75,4 +75,4 @@ yarn twenty remote list
|
||||
yarn twenty remote switch <name>
|
||||
```
|
||||
|
||||
Your credentials are stored in `~/.twenty/config.json`.
|
||||
Suas credenciais são armazenadas em `~/.twenty/config.json`.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Build, test, and ship your app — CLI commands, integration tests, CI, and publishing to a server or to npm.
|
||||
title: Visão Geral
|
||||
description: Crie, teste e envie seu app — comandos de CLI, testes de integração, CI e publicação em um servidor ou no npm.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
The **operations layer** is everything you do *to* your app rather than *with* it: invoking CLI commands, running integration tests against a real Twenty server, configuring CI, and shipping releases — either as a tarball deployed to a single server or as an npm package listed in the marketplace.
|
||||
A **camada de operações** é tudo o que você faz *para* o seu app em vez de *com* ele: invocar comandos de CLI, executar testes de integração contra um servidor Twenty real, configurar CI e enviar versões — seja como um tarball implantado em um único servidor ou como um pacote npm listado no marketplace.
|
||||
|
||||
```text
|
||||
develop ─▶ test ─▶ build ─▶ deploy / publish
|
||||
@@ -14,16 +14,16 @@ The **operations layer** is everything you do *to* your app rather than *with* i
|
||||
dev build yarn twenty publish (npm → marketplace)
|
||||
```
|
||||
|
||||
## In this section
|
||||
## Nesta seção
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="CLI" icon="terminal" href="/l/pt/developers/extend/apps/operations/cli">
|
||||
`yarn twenty` reference — exec, logs, uninstall, remotes.
|
||||
</Card>
|
||||
<Card title="Testing" icon="flask" href="/l/pt/developers/extend/apps/operations/testing">
|
||||
Vitest setup, integration tests, type checking, CI workflow.
|
||||
<Card title="Testes" icon="flask" href="/l/pt/developers/extend/apps/operations/testing">
|
||||
Configuração do Vitest, testes de integração, verificação de tipos, fluxo de trabalho de CI.
|
||||
</Card>
|
||||
<Card title="Publishing" icon="upload" href="/l/pt/developers/extend/apps/operations/publishing">
|
||||
Build, deploy a tarball, publish to npm, install.
|
||||
<Card title="Publicação" icon="carregar" href="/l/pt/developers/extend/apps/operations/publishing">
|
||||
Criar, implantar um tarball, publicar no npm, instalar.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
---
|
||||
title: Publishing
|
||||
icon: upload
|
||||
description: Distribute your Twenty app to the marketplace or deploy it internally.
|
||||
title: Publicação
|
||||
icon: carregar
|
||||
description: Distribua seu aplicativo Twenty no Marketplace ou implante-o internamente.
|
||||
---
|
||||
|
||||
## Overview
|
||||
## Visão Geral
|
||||
|
||||
Once your app is [built and tested locally](/l/pt/developers/extend/apps/getting-started/concepts), you have two paths for distributing it:
|
||||
Depois que seu aplicativo estiver [compilado e testado localmente](/l/pt/developers/extend/apps/getting-started/concepts), você tem dois caminhos para distribuí-lo:
|
||||
|
||||
* **Deploy a tarball** — upload your app directly to a specific Twenty server for internal or private use.
|
||||
* **Publish to npm** — list your app in the Twenty marketplace for any workspace to discover and install.
|
||||
* **Implantar um tarball** — envie seu aplicativo diretamente para um servidor Twenty específico para uso interno ou privado.
|
||||
* **Publicar no npm** — liste seu aplicativo no Marketplace da Twenty para que qualquer espaço de trabalho possa descobrir e instalar.
|
||||
|
||||
Both paths start from the same **build** step.
|
||||
Ambos os caminhos começam na mesma etapa de **build**.
|
||||
|
||||
## Building your app
|
||||
## Compilando seu app
|
||||
|
||||
Run the build command to compile your app and generate a distribution-ready `manifest.json`:
|
||||
Execute o comando build para compilar seu app e gerar um `manifest.json` pronto para distribuição:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty build
|
||||
```
|
||||
|
||||
This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the deploy command.
|
||||
Isso compila seu código-fonte em TypeScript, transpila funções de lógica e componentes de front-end e grava tudo em `.twenty/output/`. Adicione `--tarball` para também gerar um pacote `.tgz` para distribuição manual ou para o comando de deploy.
|
||||
|
||||
## Deploying to a server (tarball)
|
||||
## Implantando em um servidor (tarball)
|
||||
|
||||
For apps you don't want publicly available — proprietary tools, enterprise-only integrations, or experimental builds — you can deploy a tarball directly to a Twenty server.
|
||||
Para aplicativos que você não quer disponibilizar publicamente — ferramentas proprietárias, integrações apenas para empresas ou builds experimentais — você pode implantar um tarball diretamente em um servidor Twenty.
|
||||
|
||||
### Prerequisites
|
||||
### Pré-requisitos
|
||||
|
||||
Before deploying, you need a configured remote pointing to the target server. Remotes store the server URL and authentication credentials locally in `~/.twenty/config.json`.
|
||||
Antes de implantar, você precisa de um remote configurado apontando para o servidor de destino. Os remotes armazenam a URL do servidor e as credenciais de autenticação localmente em `~/.twenty/config.json`.
|
||||
|
||||
Add a remote:
|
||||
Adicionar um remote:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty remote add --api-url https://your-twenty-server.com --as production
|
||||
```
|
||||
|
||||
### Deploying
|
||||
### Implantando
|
||||
|
||||
Build and upload your app to the server in one step:
|
||||
Compile e envie seu aplicativo para o servidor em uma única etapa:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty deploy
|
||||
@@ -47,39 +47,39 @@ yarn twenty deploy
|
||||
# yarn twenty deploy --remote production
|
||||
```
|
||||
|
||||
### Sharing a deployed app
|
||||
### Compartilhando um aplicativo implantado
|
||||
|
||||
<Warning>
|
||||
Sharing private (tarball) apps across workspaces is an **Enterprise** feature. The **Distribution** tab will show an upgrade prompt instead of the share controls until your workspace has a valid Enterprise key. See [Settings > Admin Panel > Enterprise](/settings/admin-panel#enterprise) to activate it.
|
||||
Compartilhar aplicativos privados (tarball) entre espaços de trabalho é um recurso do plano **Enterprise**. A guia **Distribution** exibirá um aviso de atualização em vez dos controles de compartilhamento até que seu espaço de trabalho tenha uma chave Enterprise válida. Vá para [Configurações > Painel de Administração > Enterprise](/settings/admin-panel#enterprise) para ativá-lo.
|
||||
</Warning>
|
||||
|
||||
Tarball apps are not listed in the public marketplace, so other workspaces on the same server won't discover them by browsing. Once your workspace is on the Enterprise plan, you can share a deployed app like this:
|
||||
Aplicativos em tarball não são listados no marketplace público, então outros espaços de trabalho no mesmo servidor não os descobrirão ao navegar. Assim que o seu espaço de trabalho estiver no plano Enterprise, você pode compartilhar um app implantado desta forma:
|
||||
|
||||
1. Go to **Settings > Applications > Registrations** and open your app
|
||||
2. In the **Distribution** tab, click **Copy share link**
|
||||
3. Share this link with users on other workspaces — it takes them directly to the app's install page
|
||||
1. Vá para **Configurações > Aplicações > Registros** e abra seu aplicativo
|
||||
2. Na guia **Distribuição**, clique em **Copiar link de compartilhamento**
|
||||
3. Compartilhe esse link com usuários de outros espaços de trabalho — ele os leva diretamente para a página de instalação do aplicativo
|
||||
|
||||
The share link uses the server's base URL (without any workspace subdomain) so it works for any workspace on the server.
|
||||
O link de compartilhamento usa a URL base do servidor (sem qualquer subdomínio de espaço de trabalho), para funcionar em qualquer espaço de trabalho no servidor.
|
||||
|
||||
### Version management
|
||||
### Gerenciamento de versões
|
||||
|
||||
When updating an already deployed tarball app, the server requires the `version` in `package.json` to be **strictly higher** (per [semver](https://semver.org) ordering) than the currently deployed version. Re-deploying the same version, or pushing a lower one, is rejected before the tarball is stored — you'll see a `VERSION_ALREADY_EXISTS` error from the CLI.
|
||||
Ao atualizar um aplicativo empacotado como tarball já implantado, o servidor exige que o `version` no `package.json` seja **estritamente maior** (de acordo com a ordenação do [semver](https://semver.org)) do que a versão atualmente implantada. Reimplantar a mesma versão, ou enviar uma inferior, é rejeitado antes que o tarball seja armazenado — você verá um erro `VERSION_ALREADY_EXISTS` na CLI.
|
||||
|
||||
To release an update:
|
||||
Para lançar uma atualização:
|
||||
|
||||
1. Bump the `version` field in your `package.json` (e.g. `1.2.3` → `1.2.4`, `1.3.0`, or `2.0.0`)
|
||||
2. Run `yarn twenty deploy` (or `yarn twenty deploy --remote production`)
|
||||
3. Workspaces that have the app installed will see the upgrade available in their settings
|
||||
1. Atualize o campo `version` no seu `package.json` (por exemplo, `1.2.3` → `1.2.4`, `1.3.0` ou `2.0.0`)
|
||||
2. Execute `yarn twenty deploy` (ou `yarn twenty deploy --remote production`)
|
||||
3. Os espaços de trabalho que têm o aplicativo instalado verão a atualização disponível em suas configurações
|
||||
|
||||
<Note>
|
||||
Pre-release tags work as expected: bumping `1.0.0-rc.1` → `1.0.0-rc.2` is allowed, and a final release like `1.0.0` is correctly recognized as higher than `1.0.0-rc.5`. The version in `package.json` must itself be a valid semver string.
|
||||
Tags de pré-lançamento funcionam como esperado: incrementar `1.0.0-rc.1` → `1.0.0-rc.2` é permitido, e uma versão final como `1.0.0` é corretamente reconhecida como superior a `1.0.0-rc.5`. A versão em `package.json` deve ser, ela própria, uma string semver válida.
|
||||
</Note>
|
||||
|
||||
{/* TODO: add screenshot of the Upgrade button */}
|
||||
|
||||
### Server version compatibility
|
||||
### Compatibilidade da versão do servidor
|
||||
|
||||
If your app uses a feature introduced in a specific Twenty server version (for example, OAuth providers added in v2.3.0), you should declare the minimum server version your app requires using the `engines.twenty` field in `package.json`:
|
||||
Se o seu aplicativo usar um recurso introduzido em uma versão específica do servidor Twenty (por exemplo, provedores OAuth adicionados na v2.3.0), você deve declarar a versão mínima do servidor que seu aplicativo requer usando o campo `engines.twenty` em `package.json`:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -92,83 +92,83 @@ If your app uses a feature introduced in a specific Twenty server version (for e
|
||||
}
|
||||
```
|
||||
|
||||
The value is a standard [semver range](https://github.com/npm/node-semver#ranges). Common patterns:
|
||||
O valor é um [intervalo semver](https://github.com/npm/node-semver#ranges) padrão. Padrões comuns:
|
||||
|
||||
| Range | Meaning |
|
||||
| ---------------------------------- | ------------------------------------------ |
|
||||
| `>=2.3.0` | Any server from 2.3.0 onward |
|
||||
| `>=2.3.0 \<3.0.0` | 2.3.0 or later, but below the next major |
|
||||
| `^2.3.0` | Same as `>=2.3.0 \<3.0.0` |
|
||||
| Intervalo | Significado |
|
||||
| ---------------------------------- | ---------------------------------------------------------- |
|
||||
| `>=2.3.0` | Qualquer servidor a partir de 2.3.0 |
|
||||
| `>=2.3.0 \<3.0.0` | 2.3.0 ou posterior, mas abaixo da próxima versão principal |
|
||||
| `^2.3.0` | O mesmo que `>=2.3.0 \<3.0.0` |
|
||||
|
||||
**What happens at deploy and install time:**
|
||||
**O que acontece no momento da implantação e da instalação:**
|
||||
|
||||
* If `engines.twenty` is set and the target server's version does not satisfy the range, the deploy (tarball upload) or install is rejected with a `SERVER_VERSION_INCOMPATIBLE` error and a message indicating both the required range and the actual server version.
|
||||
* If `engines.twenty` is **not set**, the app is accepted on any server version (backward-compatible with existing apps).
|
||||
* If the server has no `APP_VERSION` configured, the check is skipped.
|
||||
* Se `engines.twenty` estiver definido e a versão do servidor de destino não satisfizer o intervalo, a implantação (upload do tarball) ou a instalação será rejeitada com o erro `SERVER_VERSION_INCOMPATIBLE` e uma mensagem indicando tanto o intervalo exigido quanto a versão real do servidor.
|
||||
* Se `engines.twenty` **não estiver definido**, o aplicativo é aceito em qualquer versão do servidor (retrocompatível com os aplicativos existentes).
|
||||
* Se o servidor não tiver `APP_VERSION` configurado, a verificação será ignorada.
|
||||
|
||||
<Note>
|
||||
The server is the authoritative check — it validates `engines.twenty` on both tarball upload and workspace install. If you deploy a tarball out-of-band or install from the marketplace, the server still enforces compatibility.
|
||||
O servidor realiza a verificação definitiva — ele valida `engines.twenty` tanto no upload do tarball quanto na instalação no workspace. Se você implantar um tarball fora de banda ou instalar a partir do marketplace, o servidor ainda impõe a compatibilidade.
|
||||
</Note>
|
||||
|
||||
## Automated CI/CD (scaffolded workflows)
|
||||
## CI/CD automatizado (fluxos de trabalho pré-configurados)
|
||||
|
||||
Apps generated with `create-twenty-app` ship with two GitHub Actions workflows out of the box, under `.github/workflows/`. They are ready to run as soon as you push the repo to GitHub — no extra setup is needed for CI, and CD only requires a single secret.
|
||||
Os apps gerados com `create-twenty-app` já vêm com dois fluxos de trabalho do GitHub Actions prontos, em `.github/workflows/`. Eles estão prontos para executar assim que você fizer push do repositório para o GitHub — nenhuma configuração extra é necessária para CI, e CD requer apenas um único segredo.
|
||||
|
||||
### CI — `ci.yml`
|
||||
|
||||
Runs integration tests on every push to `main` and every pull request.
|
||||
Executa testes de integração a cada push para `main` e a cada pull request.
|
||||
|
||||
**What it does:**
|
||||
**O que faz:**
|
||||
|
||||
1. Checks out your app's source.
|
||||
2. Spawns an isolated Twenty test instance using the `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` composite action (the CI equivalent of `yarn twenty server start --test`).
|
||||
3. Enables Corepack, sets up Node.js from your `.nvmrc`, and installs dependencies with `yarn install --immutable`.
|
||||
4. Runs `yarn test`, passing `TWENTY_API_URL` and `TWENTY_API_KEY` from the spawned instance so your tests can talk to a real server.
|
||||
1. Faz checkout do código-fonte do seu app.
|
||||
2. Inicia uma instância de teste do Twenty isolada usando a ação composta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (o equivalente em CI de `yarn twenty server start --test`).
|
||||
3. Habilita o Corepack, configura o Node.js a partir do seu `.nvmrc` e instala as dependências com `yarn install --immutable`.
|
||||
4. Executa `yarn test`, passando `TWENTY_API_URL` e `TWENTY_API_KEY` da instância iniciada para que seus testes possam se comunicar com um servidor real.
|
||||
|
||||
**Config knobs:**
|
||||
**Opções de configuração:**
|
||||
|
||||
* `TWENTY_VERSION` (env, defaults to `latest`) — pin the Twenty server version used in CI by editing this in `ci.yml`.
|
||||
* Concurrency is grouped by `github.ref` and cancels in-progress runs on new pushes.
|
||||
* `TWENTY_VERSION` (env, padrão `latest`) — fixe a versão do servidor Twenty usada no CI editando isto em `ci.yml`.
|
||||
* A concorrência é agrupada por `github.ref` e cancela execuções em andamento quando há novos pushes.
|
||||
|
||||
No secrets are required — the test instance is ephemeral and lives only for the duration of the job.
|
||||
Nenhum segredo é necessário — a instância de teste é efêmera e existe apenas durante a execução do job.
|
||||
|
||||
### CD — `cd.yml`
|
||||
|
||||
Deploys your app to a configured Twenty server on every push to `main`, and optionally from a pull request when the `deploy` label is applied.
|
||||
Faz o deploy do seu app para um servidor Twenty configurado a cada push para `main` e, opcionalmente, a partir de um pull request quando o rótulo `deploy` é aplicado.
|
||||
|
||||
**What it does:**
|
||||
**O que faz:**
|
||||
|
||||
1. Checks out the PR head (for labeled PRs) or the pushed commit.
|
||||
2. Runs `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — the CI equivalent of `yarn twenty deploy`.
|
||||
3. Runs `twentyhq/twenty/.github/actions/install-twenty-app@main` so the newly deployed version is installed into the target workspace.
|
||||
1. Faz checkout do head do PR (para PRs rotulados) ou do commit enviado.
|
||||
2. Executa `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — o equivalente em CI de `yarn twenty deploy`.
|
||||
3. Executa `twentyhq/twenty/.github/actions/install-twenty-app@main` para que a versão recém-implantada seja instalada no workspace de destino.
|
||||
|
||||
**Required configuration:**
|
||||
**Configuração obrigatória:**
|
||||
|
||||
| Setting | Where | Purpose |
|
||||
| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `TWENTY_DEPLOY_URL` | `env` in `cd.yml` (defaults to `http://localhost:3000`) | The Twenty server to deploy to. Change this to your real server URL before first use. |
|
||||
| `TWENTY_DEPLOY_API_KEY` | GitHub repo **Settings → Secrets and variables → Actions** | API key with deploy permission on the target server. |
|
||||
| Configuração | Onde | Finalidade |
|
||||
| ----------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `TWENTY_DEPLOY_URL` | `env` em `cd.yml` (padrão `http://localhost:3000`) | O servidor Twenty para o qual fazer o deploy. Altere isto para a URL real do seu servidor antes do primeiro uso. |
|
||||
| `TWENTY_DEPLOY_API_KEY` | Repositório do GitHub **Settings → Secrets and variables → Actions** | Chave de API com permissão de deploy no servidor de destino. |
|
||||
|
||||
<Note>
|
||||
The default `TWENTY_DEPLOY_URL` of `http://localhost:3000` is a placeholder — it will not reach anything from a GitHub-hosted runner. Update it to your server's public URL (or use a self-hosted runner with network access) before enabling CD.
|
||||
O `TWENTY_DEPLOY_URL` padrão de `http://localhost:3000` é um placeholder — ele não alcançará nada a partir de um runner hospedado pelo GitHub. Atualize-o para a URL pública do seu servidor (ou use um runner self-hosted com acesso à rede) antes de habilitar o CD.
|
||||
</Note>
|
||||
|
||||
**Triggering a preview deploy from a PR:**
|
||||
**Acionando um deploy de pré-visualização a partir de um PR:**
|
||||
|
||||
Add the `deploy` label to a pull request. The `if:` guard in `cd.yml` will run the job for that PR using the PR's head commit, letting you validate a change on the target server before merging.
|
||||
Adicione o rótulo `deploy` a um pull request. A condição `if:` em `cd.yml` executará o job para esse PR usando o commit HEAD do PR, permitindo que você valide uma alteração no servidor de destino antes de fazer o merge.
|
||||
|
||||
### Pinning the reusable actions
|
||||
### Fixando as ações reutilizáveis
|
||||
|
||||
Both workflows reference reusable actions at `@main`, so action updates in the `twentyhq/twenty` repo are picked up automatically. If you want deterministic builds, replace `@main` with a commit SHA or release tag on each `uses:` line.
|
||||
Ambos os fluxos de trabalho fazem referência a ações reutilizáveis em `@main`, portanto as atualizações de ações no repositório `twentyhq/twenty` são aplicadas automaticamente. Se você quiser builds determinísticos, substitua `@main` por um SHA de commit ou uma tag de release em cada linha `uses:`.
|
||||
|
||||
## Publishing to npm
|
||||
## Publicação no npm
|
||||
|
||||
Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twenty workspace can browse, install, and upgrade marketplace apps directly from the UI.
|
||||
Publicar no npm torna seu aplicativo descobrível no Marketplace da Twenty. Qualquer espaço de trabalho da Twenty pode navegar, instalar e atualizar aplicativos do Marketplace diretamente pela UI.
|
||||
|
||||
### Requirements
|
||||
### Requisitos
|
||||
|
||||
* An [npm](https://www.npmjs.com) account
|
||||
* The `twenty-app` keyword in your `package.json` `keywords` array (add it manually — it is not included by default in the `create-twenty-app` template)
|
||||
* Uma conta no [npm](https://www.npmjs.com)
|
||||
* A palavra-chave `twenty-app` no array `keywords` do seu `package.json` (adicione-a manualmente — não é incluída por padrão no template `create-twenty-app`)
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -178,9 +178,9 @@ Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twe
|
||||
}
|
||||
```
|
||||
|
||||
### Marketplace metadata
|
||||
### Metadados do Marketplace
|
||||
|
||||
The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logoUrl` and `screenshots` to reference images from the `public/` folder:
|
||||
A configuração `defineApplication()` oferece suporte a campos opcionais que controlam como seu app aparece no marketplace. Use `logoUrl` e `screenshots` para referenciar imagens da pasta `public/`:
|
||||
|
||||
```ts src/application-config.ts
|
||||
export default defineApplication({
|
||||
@@ -196,33 +196,33 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
See the [defineApplication accordion](/l/pt/developers/extend/apps/config/application#marketplace-metadata) in the Building Apps page for the full list of marketplace fields (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).
|
||||
Veja o [acordeão de defineApplication](/l/pt/developers/extend/apps/config/application#marketplace-metadata) na página Building Apps para a lista completa de campos do marketplace (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.).
|
||||
|
||||
#### Recommended screenshot dimensions
|
||||
#### Dimensões recomendadas para capturas de tela
|
||||
|
||||
The marketplace renders `screenshots` in a fixed `8:5` container (for example, `1600×1000 px`).
|
||||
O marketplace renderiza `screenshots` em um contêiner fixo de `8:5` (por exemplo, `1600×1000 px`).
|
||||
|
||||
<Note>
|
||||
Screenshots of any aspect ratio are displayed in full and are never cropped, but anything significantly taller or narrower than `8:5` will show empty bands on the sides.
|
||||
Capturas de tela de qualquer proporção são exibidas por completo e nunca são cortadas, mas qualquer coisa significativamente mais alta ou mais estreita que `8:5` exibirá faixas vazias nas laterais.
|
||||
</Note>
|
||||
|
||||
### Publish
|
||||
### Publicar
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty publish
|
||||
```
|
||||
|
||||
To publish under a specific dist-tag (e.g., `beta` or `next`):
|
||||
Para publicar sob uma dist-tag específica (por exemplo, `beta` ou `next`):
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty publish --tag beta
|
||||
```
|
||||
|
||||
### How marketplace discovery works
|
||||
### Como funciona a descoberta no marketplace
|
||||
|
||||
The Twenty server syncs its marketplace catalog from the npm registry **every hour**.
|
||||
O servidor Twenty sincroniza seu catálogo do marketplace a partir do registro do npm **a cada hora**.
|
||||
|
||||
You can trigger the sync immediately instead of waiting:
|
||||
Você pode acionar a sincronização imediatamente em vez de esperar:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty server catalog-sync
|
||||
@@ -230,15 +230,15 @@ yarn twenty server catalog-sync
|
||||
# yarn twenty server catalog-sync --remote production
|
||||
```
|
||||
|
||||
The metadata shown in the marketplace comes from your `defineApplication()` config — fields like `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, and `termsUrl`.
|
||||
Os metadados exibidos no marketplace vêm da sua configuração `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`.
|
||||
|
||||
<Note>
|
||||
If your app does not define an `aboutDescription` in `defineApplication()`, the marketplace will automatically use your package's `README.md` from npm as the about page content. This means you can maintain a single README for both npm and the Twenty marketplace. If you want a different description in the marketplace, explicitly set `aboutDescription`.
|
||||
Se o seu aplicativo não definir um `aboutDescription` em `defineApplication()`, o marketplace usará automaticamente o `README.md` do seu pacote no npm como conteúdo da página Sobre. Isso significa que você pode manter um único README tanto para o npm quanto para o marketplace da Twenty. Se quiser uma descrição diferente no marketplace, defina explicitamente `aboutDescription`.
|
||||
</Note>
|
||||
|
||||
### CI publishing
|
||||
### Publicação via CI
|
||||
|
||||
Use this GitHub Actions workflow to publish automatically on every release (uses [OIDC](https://docs.npmjs.com/trusted-publishers)):
|
||||
Use este workflow do GitHub Actions para publicar automaticamente a cada release (usa [OIDC](https://docs.npmjs.com/trusted-publishers)):
|
||||
|
||||
```yaml filename=".github/workflows/publish.yml"
|
||||
name: Publish
|
||||
@@ -265,31 +265,31 @@ jobs:
|
||||
working-directory: .twenty/output
|
||||
```
|
||||
|
||||
For other CI systems (GitLab CI, CircleCI, etc.), the same three commands apply: `yarn install`, `yarn twenty build`, then `npm publish` from `.twenty/output`.
|
||||
Para outros sistemas de CI (GitLab CI, CircleCI etc.), aplicam-se os mesmos três comandos: `yarn install`, `yarn twenty build` e, em seguida, `npm publish` a partir de `.twenty/output`.
|
||||
|
||||
<Note>
|
||||
**npm provenance** is optional but recommended. Publishing with `--provenance` adds a trust badge to your npm listing, letting users verify the package was built from a specific commit in a public CI pipeline. See the [npm provenance docs](https://docs.npmjs.com/generating-provenance-statements) for setup instructions.
|
||||
**Proveniência do npm** é opcional, mas recomendada. Publicar com `--provenance` adiciona um selo de confiança à sua listagem no npm, permitindo que os usuários verifiquem que o pacote foi construído a partir de um commit específico em um pipeline de CI público. Consulte a [documentação de proveniência do npm](https://docs.npmjs.com/generating-provenance-statements) para instruções de configuração.
|
||||
</Note>
|
||||
|
||||
## Installing apps
|
||||
## Instalando aplicativos
|
||||
|
||||
Once an app is published (npm) or deployed (tarball), workspaces can install it through the UI.
|
||||
Depois que um app é publicado (npm) ou implantado (tarball), os espaços de trabalho podem instalá-lo pela interface do usuário.
|
||||
|
||||
Go to the **Settings > Applications** page in Twenty, where both marketplace and tarball-deployed apps can be browsed and installed.
|
||||
Vá para a página **Configurações > Aplicações** no Twenty, onde é possível navegar e instalar tanto apps do marketplace quanto apps implantados por tarball.
|
||||
|
||||
{/* TODO: add screenshot of the UI when the app is registered */}
|
||||
|
||||
You can also install apps from the command line:
|
||||
Você também pode instalar apps pela linha de comando:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty install
|
||||
```
|
||||
|
||||
<Note>
|
||||
The server enforces semver versioning on install, mirroring the rules on deploy:
|
||||
O servidor impõe o versionamento semver na instalação, espelhando as regras da implantação:
|
||||
|
||||
* Installing the same version that is already installed in your workspace is rejected with an `APP_ALREADY_INSTALLED` error.
|
||||
* Installing a lower version than the one currently installed is rejected with a `CANNOT_DOWNGRADE_APPLICATION` error.
|
||||
* Instalar a mesma versão que já está instalada no seu espaço de trabalho é rejeitado com um erro `APP_ALREADY_INSTALLED`.
|
||||
* Instalar uma versão inferior à atualmente instalada é rejeitado com um erro `CANNOT_DOWNGRADE_APPLICATION`.
|
||||
|
||||
To install a newer version, deploy or publish it first, then re-run `yarn twenty install`.
|
||||
Para instalar uma versão mais recente, implante ou publique-a primeiro e, em seguida, execute novamente `yarn twenty install`.
|
||||
</Note>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
---
|
||||
title: Testing
|
||||
description: Vitest setup, integration tests against a real Twenty server, type checking, and CI with GitHub Actions.
|
||||
title: Testes
|
||||
description: Configuração do Vitest, testes de integração contra um servidor Twenty real, verificação de tipos e CI com GitHub Actions.
|
||||
icon: flask
|
||||
---
|
||||
|
||||
The SDK provides programmatic APIs that let you build, deploy, install, and uninstall your app from test code. Combined with [Vitest](https://vitest.dev/) and the typed API clients, you can write integration tests that verify your app works end-to-end against a real Twenty server.
|
||||
O SDK fornece APIs programáticas que permitem compilar, implantar, instalar e desinstalar seu aplicativo a partir de código de teste. Em conjunto com [Vitest](https://vitest.dev/) e os clientes de API tipados, você pode escrever testes de integração que verificam que seu aplicativo funciona de ponta a ponta em um servidor Twenty real.
|
||||
|
||||
## Using npm packages
|
||||
## Usando pacotes npm
|
||||
|
||||
You can install and use any npm package in your app. Both logic functions and front components are bundled with [esbuild](https://esbuild.github.io/), which inlines all dependencies into the output — no `node_modules` are needed at runtime.
|
||||
Você pode instalar e usar qualquer pacote npm no seu app. Tanto funções lógicas quanto componentes de front-end são empacotados com [esbuild](https://esbuild.github.io/), que incorpora todas as dependências na saída — nenhum `node_modules` é necessário em tempo de execução.
|
||||
|
||||
### Installing a package
|
||||
### Instalando um pacote
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add axios
|
||||
```
|
||||
|
||||
Then import it in your code:
|
||||
Em seguida, importe-o no seu código:
|
||||
|
||||
```ts src/logic-functions/fetch-data.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
@@ -37,7 +37,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
The same works for front components:
|
||||
O mesmo vale para componentes de front-end:
|
||||
|
||||
```tsx src/front-components/chart.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -54,25 +54,25 @@ export default defineFrontComponent({
|
||||
});
|
||||
```
|
||||
|
||||
### How bundling works
|
||||
### Como o empacotamento funciona
|
||||
|
||||
The build step uses esbuild to produce a single self-contained file per logic function and per front component. All imported packages are inlined into the bundle.
|
||||
A etapa de build usa o esbuild para produzir um único arquivo independente por função lógica e por componente de front-end. Todos os pacotes importados são incorporados ao bundle.
|
||||
|
||||
**Logic functions** run in a Node.js environment. Node built-in modules (`fs`, `path`, `crypto`, `http`, etc.) are available and do not need to be installed.
|
||||
**Funções lógicas** são executadas em um ambiente Node.js. Módulos nativos do Node (`fs`, `path`, `crypto`, `http`, etc.) estão disponíveis e não precisam ser instalados.
|
||||
|
||||
**Front components** run in a Web Worker. Node built-in modules are **not** available — only browser APIs and npm packages that work in a browser environment.
|
||||
**Componentes de front-end** são executados em um Web Worker. Módulos nativos do Node **não** estão disponíveis — apenas APIs do navegador e pacotes npm que funcionam em um ambiente de navegador.
|
||||
|
||||
Both environments have `twenty-client-sdk/core` and `twenty-client-sdk/metadata` available as pre-provided modules — these are not bundled but resolved at runtime by the server.
|
||||
Ambos os ambientes têm `twenty-client-sdk/core` e `twenty-client-sdk/metadata` disponíveis como módulos pré-fornecidos — eles não são empacotados, mas resolvidos em tempo de execução pelo servidor.
|
||||
|
||||
## Setup
|
||||
## Configuração
|
||||
|
||||
The scaffolded app already includes Vitest. If you set it up manually, install the dependencies:
|
||||
O aplicativo gerado pelo scaffolder já inclui o Vitest. Se você configurá-lo manualmente, instale as dependências:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D vitest vite-tsconfig-paths
|
||||
```
|
||||
|
||||
Create a `vitest.config.ts` at the root of your app:
|
||||
Crie um `vitest.config.ts` na raiz do seu aplicativo:
|
||||
|
||||
```ts vitest.config.ts
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
@@ -98,7 +98,7 @@ export default defineConfig({
|
||||
});
|
||||
```
|
||||
|
||||
Create a setup file that verifies the server is reachable before tests run:
|
||||
Crie um arquivo de configuração que verifique se o servidor está acessível antes da execução dos testes:
|
||||
|
||||
```ts src/__tests__/setup-test.ts
|
||||
import * as fs from 'fs';
|
||||
@@ -138,22 +138,22 @@ beforeAll(async () => {
|
||||
});
|
||||
```
|
||||
|
||||
## Programmatic SDK APIs
|
||||
## APIs programáticas do SDK
|
||||
|
||||
The `twenty-sdk/cli` subpath exports functions you can call directly from test code:
|
||||
O subcaminho `twenty-sdk/cli` exporta funções que você pode chamar diretamente a partir do código de teste:
|
||||
|
||||
| Function | Description |
|
||||
| -------------- | ------------------------------------------- |
|
||||
| `appBuild` | Build the app and optionally pack a tarball |
|
||||
| `appDeploy` | Upload a tarball to the server |
|
||||
| `appInstall` | Install the app on the active workspace |
|
||||
| `appUninstall` | Uninstall the app from the active workspace |
|
||||
| Função | Descrição |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball |
|
||||
| `appDeploy` | Enviar um tarball para o servidor |
|
||||
| `appInstall` | Instalar o aplicativo no espaço de trabalho ativo |
|
||||
| `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo |
|
||||
|
||||
Each function returns a result object with `success: boolean` and either `data` or `error`.
|
||||
Cada função retorna um objeto de resultado com `success: boolean` e `data` ou `error`.
|
||||
|
||||
## Writing an integration test
|
||||
## Escrevendo um teste de integração
|
||||
|
||||
Here is a full example that builds, deploys, and installs the app, then verifies it appears in the workspace:
|
||||
Aqui está um exemplo completo que compila, implanta e instala o aplicativo e, em seguida, verifica se ele aparece no espaço de trabalho:
|
||||
|
||||
```ts src/__tests__/app-install.integration-test.ts
|
||||
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
|
||||
@@ -216,40 +216,40 @@ describe('App installation', () => {
|
||||
});
|
||||
```
|
||||
|
||||
## Running tests
|
||||
## Executando testes
|
||||
|
||||
Make sure your local Twenty server is running, then:
|
||||
Certifique-se de que seu servidor Twenty local esteja em execução e, em seguida:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test
|
||||
```
|
||||
|
||||
Or in watch mode during development:
|
||||
Ou no modo watch durante o desenvolvimento:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test:watch
|
||||
```
|
||||
|
||||
## Type checking
|
||||
## Verificação de tipos
|
||||
|
||||
You can also run type checking on your app without running tests:
|
||||
Você também pode executar a verificação de tipos no seu aplicativo sem executar os testes:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty typecheck
|
||||
```
|
||||
|
||||
This runs `tsc --noEmit` and reports any type errors.
|
||||
Isso executa `tsc --noEmit` e informa quaisquer erros de tipo.
|
||||
|
||||
## CI with GitHub Actions
|
||||
## CI com GitHub Actions
|
||||
|
||||
The scaffolder generates a ready-to-use GitHub Actions workflow at `.github/workflows/ci.yml`. It runs your integration tests automatically on every push to `main` and on pull requests.
|
||||
O gerador de scaffold cria um workflow do GitHub Actions pronto para uso em `.github/workflows/ci.yml`. Ele executa seus testes de integração automaticamente a cada push para `main` e em pull requests.
|
||||
|
||||
The workflow:
|
||||
O workflow:
|
||||
|
||||
1. Checks out your code
|
||||
2. Spins up a temporary Twenty server using the `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` action
|
||||
3. Installs dependencies with `yarn install --immutable`
|
||||
4. Runs `yarn test` with `TWENTY_API_URL` and `TWENTY_API_KEY` injected from the action outputs
|
||||
1. Faz checkout do seu código
|
||||
2. Inicializa um servidor Twenty temporário usando a ação `twentyhq/twenty/.github/actions/spawn-twenty-docker-image`
|
||||
3. Instala as dependências com `yarn install --immutable`
|
||||
4. Executa `yarn test` com `TWENTY_API_URL` e `TWENTY_API_KEY` injetados a partir das saídas da ação
|
||||
|
||||
```yaml .github/workflows/ci.yml
|
||||
name: CI
|
||||
@@ -296,6 +296,6 @@ jobs:
|
||||
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
|
||||
```
|
||||
|
||||
You don't need to configure any secrets — the `spawn-twenty-docker-image` action starts an ephemeral Twenty server directly in the runner and outputs the connection details. The `GITHUB_TOKEN` secret is provided automatically by GitHub.
|
||||
Você não precisa configurar nenhum segredo — a ação `spawn-twenty-docker-image` inicia um servidor Twenty efêmero diretamente no runner e fornece os detalhes de conexão. O segredo `GITHUB_TOKEN` é fornecido automaticamente pelo GitHub.
|
||||
|
||||
To pin a specific Twenty version instead of `latest`, change the `TWENTY_VERSION` environment variable at the top of the workflow.
|
||||
Para fixar uma versão específica do Twenty em vez de `latest`, altere a variável de ambiente `TWENTY_VERSION` no topo do workflow.
|
||||
|
||||
@@ -155,7 +155,27 @@
|
||||
"label": "Visão Geral"
|
||||
},
|
||||
"apps": {
|
||||
"label": "Aplicativos"
|
||||
"label": "Aplicativos",
|
||||
"groups": {
|
||||
"appsGettingStarted": {
|
||||
"label": "Primeiros passos"
|
||||
},
|
||||
"appsConfig": {
|
||||
"label": "Configuração"
|
||||
},
|
||||
"appsData": {
|
||||
"label": "Data"
|
||||
},
|
||||
"appsLogic": {
|
||||
"label": "Lógica"
|
||||
},
|
||||
"appsLayout": {
|
||||
"label": "Layout"
|
||||
},
|
||||
"appsOperations": {
|
||||
"label": "Operações"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"label": "API"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Application Config
|
||||
description: Declare your app's identity, default role, variables, and marketplace metadata with defineApplication.
|
||||
title: Configurația aplicației
|
||||
description: Declarați identitatea aplicației, rolul implicit, variabilele și metadatele din marketplace cu `defineApplication`.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
Every app must have exactly one `defineApplication` call. It declares:
|
||||
Fiecare aplicație trebuie să aibă exact un apel `defineApplication`. Acesta declară:
|
||||
|
||||
* **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](/l/ro/developers/extend/apps/logic/logic-functions).
|
||||
* **Identitate** — identificator universal, nume de afișare, descriere.
|
||||
* **Permisiuni** — sub ce rol rulează funcțiile logice și componentele front-end ale acesteia.
|
||||
* **Variabile** *(opțional)* — perechi cheie–valoare expuse codului dvs. ca variabile de mediu.
|
||||
* **Hook-uri de pre-instalare / post-instalare** *(opțional)* — vedeți [Funcții logice](/l/ro/developers/extend/apps/logic/logic-functions).
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
@@ -31,35 +31,35 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
Notițe:
|
||||
|
||||
* `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 (e.g., `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` must reference a role defined with [`defineRole()`](/l/ro/developers/extend/apps/config/roles).
|
||||
* Pre-install and post-install functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
* Câmpurile `universalIdentifier` sunt ID-uri deterministe pe care le dețineți. Generați-le o singură dată și mențineți-le stabile între sincronizări.
|
||||
* `applicationVariables` devin variabile de mediu pentru funcțiile și componentele front-end (de exemplu, `DEFAULT_RECIPIENT_NAME` este disponibil ca `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` trebuie să facă referire la un rol definit cu [`defineRole()`](/l/ro/developers/extend/apps/config/roles).
|
||||
* Funcțiile de pre-instalare și post-instalare sunt detectate automat în timpul construirii manifestului — nu trebuie să le referiți în `defineApplication()`.
|
||||
|
||||
## Default function role
|
||||
## Rol implicit pentru funcții
|
||||
|
||||
The `defaultRoleUniversalIdentifier` controls what the app's logic functions and front components can access:
|
||||
`defaultRoleUniversalIdentifier` controlează la ce pot avea acces funcțiile logice și componentele front-end ale aplicației:
|
||||
|
||||
* The runtime token injected as `TWENTY_APP_ACCESS_TOKEN` is derived from this role.
|
||||
* The typed API client is restricted to the permissions granted to that role.
|
||||
* Follow least-privilege: declare only the permissions your functions need.
|
||||
* Tokenul de runtime injectat ca `TWENTY_APP_ACCESS_TOKEN` este derivat din acest rol.
|
||||
* Clientul API tipizat este restricționat la permisiunile acordate acelui rol.
|
||||
* Respectați principiul celui mai mic privilegiu: declarați doar permisiunile de care au nevoie funcțiile.
|
||||
|
||||
When you scaffold a new app, the CLI creates a starter role file at `src/roles/default-role.ts`. See [Roles & Permissions](/l/ro/developers/extend/apps/config/roles) for the full reference.
|
||||
Când generați o aplicație nouă, CLI creează un fișier de rol de pornire la `src/roles/default-role.ts`. Consultați [Roluri și permisiuni](/l/ro/developers/extend/apps/config/roles) pentru referința completă.
|
||||
|
||||
## Marketplace metadata
|
||||
## Metadate pentru marketplace
|
||||
|
||||
If you plan to [publish your app](/l/ro/developers/extend/apps/operations/publishing), these optional fields control how it appears in the marketplace:
|
||||
Dacă intenționați să [publicați aplicația](/l/ro/developers/extend/apps/operations/publishing), aceste câmpuri opționale controlează modul în care apare în marketplace:
|
||||
|
||||
| Field | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Author or company name |
|
||||
| `category` | App category for marketplace filtering |
|
||||
| `logoUrl` | Path to your app logo (e.g., `public/logo.png`) |
|
||||
| `screenshots` | Array of screenshot paths (e.g., `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm |
|
||||
| `websiteUrl` | Link to your website |
|
||||
| `termsUrl` | Link to terms of service |
|
||||
| `emailSupport` | Support email address |
|
||||
| `issueReportUrl` | Link to issue tracker |
|
||||
| Câmp | Descriere |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `author` | Numele autorului sau al companiei |
|
||||
| `category` | Categoria aplicației pentru filtrarea în marketplace |
|
||||
| `logoUrl` | Calea către logo-ul aplicației (de ex., `public/logo.png`) |
|
||||
| `screenshots` | Array de căi către capturi de ecran (de ex., `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Descriere markdown mai lungă pentru fila "About". Dacă este omis, marketplace-ul folosește `README.md` al pachetului de pe npm |
|
||||
| `websiteUrl` | Link către site-ul dvs. |
|
||||
| `termsUrl` | Link către termenii de serviciu |
|
||||
| `emailSupport` | Adresă de e-mail pentru suport |
|
||||
| `issueReportUrl` | Link către sistemul de urmărire a problemelor |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Install Hooks
|
||||
description: Run logic before or after the install — seed data, back up records, validate the upgrade.
|
||||
title: Instalare hooks
|
||||
description: Rulați logică înainte sau după instalare — pentru a popula cu date inițiale, a face copii de rezervă ale înregistrărilor, a valida actualizarea.
|
||||
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](/l/ro/developers/extend/apps/logic/logic-functions) and receive an `InstallPayload`, but they're declared with their own define functions — `definePostInstallLogicFunction()` and `definePreInstallLogicFunction()` — and live outside the normal trigger model (HTTP, cron, database events).
|
||||
Install hooks sunt funcții logice speciale care rulează în timpul ciclului de viață al instalării sau actualizării. Acestea folosesc același runtime de handler ca și [funcțiile logice](/l/ro/developers/extend/apps/logic/logic-functions) obișnuite și primesc un `InstallPayload`, dar sunt declarate cu propriile lor funcții de definire — `definePostInstallLogicFunction()` și `definePreInstallLogicFunction()` — și există în afara modelului obișnuit de declanșatori (HTTP, cron, evenimente de bază de date).
|
||||
|
||||
Each app may define **at most one pre-install** and **at most one post-install** function. The manifest build will error if more than one of either is detected.
|
||||
Fiecare aplicație poate defini **cel mult o funcție de pre-instalare** și **cel mult o funcție de post-instalare**. Construirea manifestului va genera o eroare dacă este detectată mai mult de una dintre oricare dintre ele.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -20,9 +20,9 @@ Each app may define **at most one pre-install** and **at most one post-install**
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Runs after the workspace metadata migration is applied">
|
||||
<Accordion title="definePostInstallLogicFunction" description="Rulează după ce migrarea metadatelor workspace-ului este aplicată">
|
||||
|
||||
A post-install function runs automatically once your app has finished installing on a workspace. Serverul o execută **după** ce metadatele aplicației au fost sincronizate și clientul SDK a fost generat, astfel încât spațiul de lucru este complet pregătit pentru utilizare, iar noua schemă este disponibilă. Cazuri tipice de utilizare includ popularea cu date implicite, crearea de înregistrări inițiale, configurarea setărilor spațiului de lucru sau provizionarea resurselor în cadrul serviciilor terților.
|
||||
O funcție de post-instalare rulează automat după ce aplicația a terminat de instalat într-un spațiu de lucru. Serverul o execută **după** ce metadatele aplicației au fost sincronizate și clientul SDK a fost generat, astfel încât spațiul de lucru este complet pregătit pentru utilizare, iar noua schemă este disponibilă. Cazuri tipice de utilizare includ popularea cu date implicite, crearea de înregistrări inițiale, configurarea setărilor spațiului de lucru sau provizionarea resurselor în cadrul serviciilor terților.
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -58,14 +58,14 @@ Puncte cheie:
|
||||
* Asigurați-vă că handlerul dvs. este idempotent. În modul asincron, coada poate reîncerca de până la trei ori; în oricare mod, hook-ul poate rula din nou la actualizări când `shouldRunOnVersionUpgrade: true`.
|
||||
* Variabilele de mediu `APPLICATION_ID`, `APP_ACCESS_TOKEN` și `API_URL` sunt disponibile în interiorul handlerului (la fel ca în orice altă funcție logică), astfel încât puteți apela API-ul Twenty cu un token de acces al aplicației limitat la aplicația dvs.
|
||||
* Este permisă o singură funcție de post-instalare per aplicație. Construirea manifestului va genera o eroare dacă este detectată mai mult de una.
|
||||
* The function's `universalIdentifier`, `shouldRunOnVersionUpgrade`, and `shouldRunSynchronously` are automatically attached to the application manifest under the `postInstallLogicFunction` field during the build — you do not need to reference them in [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
|
||||
* `universalIdentifier`, `shouldRunOnVersionUpgrade` și `shouldRunSynchronously` ale funcției sunt atașate automat la manifestul aplicației în câmpul `postInstallLogicFunction` în timpul build-ului — nu este nevoie să le referi în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
|
||||
* Timpul de expirare implicit este setat la 300 de secunde (5 minute) pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
|
||||
* **Nu se execută în modul dev**: când o aplicație este înregistrată local (prin `yarn twenty dev`), serverul sare complet peste fluxul de instalare și sincronizează fișierele direct prin watcher-ul CLI — astfel încât post-install nu rulează niciodată în modul dev, indiferent de `shouldRunSynchronously`. Folosiți `yarn twenty exec --postInstall` pentru a-l declanșa manual într-un workspace care rulează.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Runs before the workspace metadata migration is applied">
|
||||
<Accordion title="definePreInstallLogicFunction" description="Rulează înainte ca migrarea metadatelor workspace-ului să fie aplicată">
|
||||
|
||||
A pre-install function runs automatically during installation, **before the workspace metadata migration is applied**. Are aceeași structură a payload-ului ca post-install (`InstallPayload`), dar este plasată mai devreme în fluxul de instalare, astfel încât poate pregăti starea de care depinde migrarea iminentă — utilizări tipice includ realizarea unui backup al datelor, validarea compatibilității cu noua schemă sau arhivarea înregistrărilor care urmează să fie restructurate sau eliminate.
|
||||
O funcție de pre-instalare rulează automat în timpul instalării, **înainte ca migrarea metadatelor workspace-ului să fie aplicată**. Are aceeași structură a payload-ului ca post-install (`InstallPayload`), dar este plasată mai devreme în fluxul de instalare, astfel încât poate pregăti starea de care depinde migrarea iminentă — utilizări tipice includ realizarea unui backup al datelor, validarea compatibilității cu noua schemă sau arhivarea înregistrărilor care urmează să fie restructurate sau eliminate.
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Configure the app itself — its identity, default permissions, and what runs at install time.
|
||||
title: Prezentare generală
|
||||
description: Configurează însăși aplicația — identitatea ei, permisiunile implicite și ceea ce rulează în momentul instalării.
|
||||
icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
A Twenty app's **config layer** is what describes the app *to the platform* — its identity, the permissions it holds, and the code that runs during install or upgrade. These declarations don't add new data shapes or runtime behavior; they tell Twenty *who the app is* and *how to set it up*.
|
||||
**Stratul de configurare** al unei aplicații Twenty este cel care descrie aplicația *platformei* — identitatea ei, permisiunile pe care le deține și codul care rulează în timpul instalării sau actualizării. Aceste declarații nu adaugă noi structuri de date sau comportamente la rulare; ele îi spun lui Twenty *cine este aplicația* și *cum să fie configurată*.
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
@@ -26,26 +26,26 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## În această secțiune
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Application Config" icon="rocket" href="/l/ro/developers/extend/apps/config/application">
|
||||
`defineApplication` — identity, default role, variables, marketplace metadata.
|
||||
<Card title="Configurare aplicație" icon="rocket" href="/l/ro/developers/extend/apps/config/application">
|
||||
`defineApplication` — identitate, rol implicit, variabile, metadate pentru marketplace.
|
||||
</Card>
|
||||
<Card title="Roles & Permissions" icon="shield-halved" href="/l/ro/developers/extend/apps/config/roles">
|
||||
`defineRole` — declare what your app's logic functions can read and write.
|
||||
<Card title="Roluri și permisiuni" icon="shield-halved" href="/l/ro/developers/extend/apps/config/roles">
|
||||
`defineRole` — declară ce pot citi și scrie funcțiile logice ale aplicației tale.
|
||||
</Card>
|
||||
<Card title="Install Hooks" icon="wrench" href="/l/ro/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` and `definePostInstallLogicFunction` — back up data, seed defaults, validate upgrades.
|
||||
<Card title="Hook-uri de instalare" icon="wrench" href="/l/ro/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` și `definePostInstallLogicFunction` — fac backup la date, introduc valori implicite, validează actualizările.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## How the pieces relate
|
||||
## Cum se leagă componentele între ele
|
||||
|
||||
* **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).
|
||||
* **Aplicația** este punctul de intrare. Fiecare aplicație are exact un apel `defineApplication()`, iar acesta indică un singur **Rol** ca implicit.
|
||||
* **Rolul** controlează ce pot citi și scrie funcțiile logice și componentele de interfață ale aplicației. Urmează principiul celui mai mic privilegiu: acordă doar permisiunile de care codul tău are cu adevărat nevoie.
|
||||
* **Hook-urile de instalare** rulează în timpul instalării sau actualizării — pre-install înainte de migrarea metadatelor (astfel încât să poată refuza o actualizare riscantă), post-install după migrare (astfel încât să poată introduce date implicite în noua schemă).
|
||||
|
||||
<Note>
|
||||
Install hooks share the [logic function](/l/ro/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).
|
||||
Hook-urile de instalare împart același runtime cu [funcțiile logice](/l/ro/developers/extend/apps/logic/logic-functions) — aceeași semnătură a handler-ului, aceleași variabile de mediu, același client API tipizat — dar sunt declarate cu propriile lor funcții `define` și există în afara modelului obișnuit de declanșare (HTTP, cron, evenimente de bază de date).
|
||||
</Note>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Public Assets
|
||||
description: Ship static files — images, icons, fonts — alongside your app via the public/ folder.
|
||||
title: Resurse publice
|
||||
description: Livrați fișiere statice — imagini, icoane, fonturi — împreună cu aplicația dvs. prin folderul public/.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Roles & Permissions
|
||||
description: Declare what objects and fields your app's logic functions and front components can read and write.
|
||||
title: Roluri și permisiuni
|
||||
description: Declarați ce obiecte și câmpuri pot citi și scrie funcțiile de logică și componentele front ale aplicației dvs.
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
A **role** is a permission set: which objects an app can read or write, which fields it can see, and which platform-level capabilities it can use. Every app's logic functions and front components inherit the permissions of the role declared as `defaultRoleUniversalIdentifier` in [`defineApplication`](/l/ro/developers/extend/apps/config/application).
|
||||
Un **rol** este un set de permisiuni: ce obiecte poate citi sau scrie o aplicație, ce câmpuri poate vedea și ce capabilități la nivel de platformă poate folosi. Toate funcțiile de logică și componentele front ale unei aplicații moștenesc permisiunile rolului declarat ca `defaultRoleUniversalIdentifier` în [`defineApplication`](/l/ro/developers/extend/apps/config/application).
|
||||
|
||||
```ts src/roles/restricted-company-role.ts
|
||||
import {
|
||||
@@ -49,9 +49,9 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
## The default function role
|
||||
## Rolul implicit pentru funcții
|
||||
|
||||
When you scaffold a new app, the CLI creates a default role file:
|
||||
Când generați o aplicație nouă, CLI creează un fișier de rol implicit:
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineRole, PermissionFlag } from 'twenty-sdk/define';
|
||||
@@ -77,14 +77,14 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
This role's `universalIdentifier` is referenced from `application-config.ts` as `defaultRoleUniversalIdentifier`:
|
||||
`universalIdentifier` al acestui rol este referențiat din `application-config.ts` ca `defaultRoleUniversalIdentifier`:
|
||||
|
||||
* **`*.role.ts`** declares what the role can do.
|
||||
* **`application-config.ts`** points to that role so your functions inherit its permissions.
|
||||
* **`*.role.ts`** declară ce poate face rolul.
|
||||
* **`application-config.ts`** indică acel rol, astfel încât funcțiile moștenesc permisiunile lui.
|
||||
|
||||
## Best practices
|
||||
## Cele mai bune practici
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it — the default grants broad read access, which is rarely what you want in production.
|
||||
* Replace `objectPermissions` and `fieldPermissions` with the exact objects and fields your functions actually need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal.
|
||||
* See a working example: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
* Porniți de la rolul generat de scaffolding, apoi restrângeți-l progresiv — rolul implicit acordă acces larg la citire, ceea ce rareori este de dorit în producție.
|
||||
* Înlocuiți `objectPermissions` și `fieldPermissions` cu obiectele și câmpurile de care au nevoie efectiv funcțiile dvs.
|
||||
* `permissionFlags` controlează accesul la capabilități la nivelul platformei. Mențineți-le la minimum.
|
||||
* Vedeți un exemplu funcțional: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Extending Objects
|
||||
description: Add fields to standard Twenty objects (Person, Company, …) or to objects from other apps using defineField.
|
||||
title: Extinderea obiectelor
|
||||
description: Adăugați câmpuri la obiectele standard Twenty (Person, Company, …) sau la obiecte din alte aplicații folosind defineField.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Use `defineField()` to add a field to an object you don't own — a standard Twenty object like Person or Company, or an object shipped by another installed app. Unlike inline fields declared inside [`defineObject`](/l/ro/developers/extend/apps/data/objects), standalone fields require an `objectUniversalIdentifier` to specify which object they extend.
|
||||
Folosiți `defineField()` pentru a adăuga un câmp la un obiect pe care nu îl dețineți — un obiect standard Twenty precum Person sau Company, sau un obiect livrat de o altă aplicație instalată. Spre deosebire de câmpurile inline declarate în interiorul [`defineObject`](/l/ro/developers/extend/apps/data/objects), câmpurile independente necesită un `objectUniversalIdentifier` pentru a specifica obiectul pe care îl extind.
|
||||
|
||||
```ts src/fields/company-loyalty-tier.field.ts
|
||||
import { defineField, FieldType } from 'twenty-sdk/define';
|
||||
@@ -24,9 +24,9 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Puncte cheie
|
||||
|
||||
* `objectUniversalIdentifier` identifies the target object. For standard Twenty objects, import the constant from `twenty-sdk`:
|
||||
* `objectUniversalIdentifier` identifică obiectul țintă. Pentru obiectele standard Twenty, importați constanta din `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
@@ -37,12 +37,12 @@ export default defineField({
|
||||
// …
|
||||
```
|
||||
|
||||
* When defining fields **inline inside `defineObject()`**, you do **not** need `objectUniversalIdentifier` — it's inherited from the parent object.
|
||||
* Atunci când definiți câmpuri **inline în `defineObject()`**, **nu** aveți nevoie de `objectUniversalIdentifier` — acesta este moștenit de la obiectul părinte.
|
||||
|
||||
* `defineField()` is the only way to add fields to objects you didn't create with `defineObject()`.
|
||||
* `defineField()` este singura modalitate de a adăuga câmpuri la obiecte pe care nu le-ați creat cu `defineObject()`.
|
||||
|
||||
* File location is up to you. The convention is `src/fields/\<name>.field.ts`, but the SDK detects fields anywhere in `src/`.
|
||||
* Locația fișierului depinde de dumneavoastră. Convenția este `src/fields/\<name>.field.ts`, dar SDK-ul detectează câmpuri oriunde în `src/`.
|
||||
|
||||
## Adding a relation to an existing object
|
||||
## Adăugarea unei relații la un obiect existent
|
||||
|
||||
To add a relation field (e.g. linking your custom object to a standard `Person`), use `defineField()` with `FieldType.RELATION`. The pattern is the same as for inline relations but with `objectUniversalIdentifier` set explicitly. See [Relations](/l/ro/developers/extend/apps/data/relations) for the bidirectional pattern.
|
||||
Pentru a adăuga un câmp de tip relație (de ex. pentru a lega obiectul personalizat de un `Person` standard), folosiți `defineField()` cu `FieldType.RELATION`. Modelul este același ca pentru relațiile inline, dar cu `objectUniversalIdentifier` setat explicit. Consultați [Relații](/l/ro/developers/extend/apps/data/relations) pentru modelul bidirecțional.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Obiecte
|
||||
description: Declare new record types — custom tables with their own fields — using defineObject.
|
||||
description: Declarați noi tipuri de înregistrări — tabele personalizate cu propriile câmpuri — folosind defineObject.
|
||||
icon: tabel
|
||||
---
|
||||
|
||||
Custom **objects** are new record types your app adds to a workspace — Post Card, Invoice, Subscription, anything specific to your domain. Each object declares its schema (fields, relations, default values) and a stable universal identifier that survives across syncs and deploys.
|
||||
**Obiectele** personalizate sunt tipuri noi de înregistrări pe care aplicația dvs. le adaugă într-un spațiu de lucru — carte poștală, factură, abonament, orice este specific domeniului dvs. Fiecare obiect își declară propria schemă (câmpuri, relații, valori implicite) și un identificator universal stabil, care este păstrat între sincronizări și implementări.
|
||||
|
||||
```ts src/objects/post-card.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
@@ -79,15 +79,15 @@ export default defineObject({
|
||||
* `universalIdentifier` trebuie să fie unic și stabil între implementări.
|
||||
* Fiecare câmp necesită un `name`, un `type`, un `label` și propriul `universalIdentifier` stabil.
|
||||
* Matricea `fields` este opțională — puteți defini obiecte fără câmpuri personalizate.
|
||||
* Inline fields defined here do **not** need an `objectUniversalIdentifier` — it's inherited from the parent object. Use [`defineField()`](/l/ro/developers/extend/apps/data/extending-objects) to add fields to objects you don't own.
|
||||
* You can scaffold new objects with `yarn twenty add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/l/ro/developers/extend/apps/getting-started/scaffolding).
|
||||
* Câmpurile inline definite aici **nu** au nevoie de `objectUniversalIdentifier` — este moștenit de la obiectul părinte. Folosiți [`defineField()`](/l/ro/developers/extend/apps/data/extending-objects) pentru a adăuga câmpuri la obiecte care nu vă aparțin.
|
||||
* Puteți genera obiecte noi cu `yarn twenty add object`, care vă ghidează prin denumire, câmpuri și relații. Consultați [Arhitectură → Generarea entităților](/l/ro/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
<Note>
|
||||
**Base fields are added automatically.** When you define a custom object, Twenty creates standard fields like `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, and `deletedAt` for you. You don't need to declare them in your `fields` array — only your custom fields. You can override a default field by declaring one with the same name, but this is rarely a good idea.
|
||||
**Câmpurile de bază sunt adăugate automat.** Când definiți un obiect personalizat, Twenty creează pentru dvs. câmpuri standard precum `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` și `deletedAt`. Nu trebuie să le declarați în tabloul `fields` — doar câmpurile dvs. personalizate. Puteți suprascrie un câmp implicit declarând unul cu același nume, dar acest lucru este rareori o idee bună.
|
||||
</Note>
|
||||
|
||||
## Ce urmează
|
||||
|
||||
* **Connect this object to others** — see [Relations](/l/ro/developers/extend/apps/data/relations) for the bidirectional relation pattern.
|
||||
* **Add fields to objects from other apps** — see [Extending Objects](/l/ro/developers/extend/apps/data/extending-objects) for `defineField()`.
|
||||
* **Display this object in the UI** — see [Views](/l/ro/developers/extend/apps/layout/views) and [Navigation Menu Items](/l/ro/developers/extend/apps/layout/navigation-menu-items) to put it in the sidebar.
|
||||
* **Conectați acest obiect la altele** — consultați [Relații](/l/ro/developers/extend/apps/data/relations) pentru modelul de relație bidirecțională.
|
||||
* **Adăugați câmpuri la obiecte din alte aplicații** — consultați [Extinderea obiectelor](/l/ro/developers/extend/apps/data/extending-objects) pentru `defineField()`.
|
||||
* **Afișați acest obiect în interfața utilizator** — consultați [Vizualizări](/l/ro/developers/extend/apps/layout/views) și [Elemente de meniu de navigare](/l/ro/developers/extend/apps/layout/navigation-menu-items) pentru a-l plasa în bara laterală.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Shape the data your app adds to a workspace — objects, fields, and relations.
|
||||
title: Prezentare generală
|
||||
description: Modelați datele pe care aplicația le adaugă într-un spațiu de lucru — obiecte, câmpuri și relații.
|
||||
icon: database
|
||||
---
|
||||
|
||||
A Twenty app's **data layer** is the data your app *adds* to a workspace — the new record types it declares, the columns it adds to existing objects, and how those records connect to each other.
|
||||
Stratul de **date** al unei aplicații Twenty reprezintă datele pe care aplicația le *adaugă* într-un spațiu de lucru — noile tipuri de înregistrări pe care le declară, coloanele pe care le adaugă la obiectele existente și modul în care aceste înregistrări se conectează între ele.
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────┐
|
||||
@@ -23,30 +23,30 @@ A Twenty app's **data layer** is the data your app *adds* to a workspace — the
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## În această secțiune
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Objects" icon="table" href="/l/ro/developers/extend/apps/data/objects">
|
||||
`defineObject` — declare new record types with their own fields.
|
||||
<Card title="Obiecte" icon="table" href="/l/ro/developers/extend/apps/data/objects">
|
||||
`defineObject` — declarați noi tipuri de înregistrări cu propriile lor câmpuri.
|
||||
</Card>
|
||||
<Card title="Extending Objects" icon="wand-magic-sparkles" href="/l/ro/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — add fields to standard or other apps' objects.
|
||||
<Card title="Extinderea obiectelor" icon="wand-magic-sparkles" href="/l/ro/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — adăugați câmpuri la obiecte standard sau la obiectele altor aplicații.
|
||||
</Card>
|
||||
<Card title="Relations" icon="diagram-project" href="/l/ro/developers/extend/apps/data/relations">
|
||||
Bidirectional `MANY_TO_ONE` / `ONE_TO_MANY` connections between objects.
|
||||
<Card title="Relații" icon="diagram-project" href="/l/ro/developers/extend/apps/data/relations">
|
||||
Conexiuni bidirecționale `MANY_TO_ONE` / `ONE_TO_MANY` între obiecte.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Entities at a glance
|
||||
## Entități dintr-o privire
|
||||
|
||||
| Entity | Purpose | Defined with |
|
||||
| ------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Object** | A new custom record type (e.g. PostCard, Invoice) with its own fields | `defineObject()` |
|
||||
| **Field** | A column on an object. Standalone fields can extend objects you didn't create (e.g. add `loyaltyTier` to Company) | `defineField()` |
|
||||
| **Relation** | A bidirectional link between two objects — both sides declared as fields | `defineField()` with `FieldType.RELATION` |
|
||||
| Entitate | Scop | Definit cu |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
||||
| **Obiect** | Un nou tip de înregistrare personalizat (de ex. PostCard, Invoice) cu propriile sale câmpuri | `defineObject()` |
|
||||
| **Câmp** | O coloană pe un obiect. Câmpurile independente pot extinde obiecte pe care nu le-ați creat (de ex. adăugați `loyaltyTier` la Company) | `defineField()` |
|
||||
| **Relație** | O legătură bidirecțională între două obiecte — ambele părți declarate ca câmpuri | `defineField()` cu `FieldType.RELATION` |
|
||||
|
||||
The SDK detects these via AST analysis at build time, so file organization is up to you — the convention is `src/objects/` and `src/fields/`. Stable `universalIdentifier` UUIDs tie everything together across deploys.
|
||||
SDK-ul detectează acestea prin analiza AST la momentul build-ului, astfel încât organizarea fișierelor ține de dumneavoastră — convenția este `src/objects/` și `src/fields/`. UUID-urile stabile `universalIdentifier` leagă totul în toate implementările.
|
||||
|
||||
<Note>
|
||||
Looking for **Application Config** or **Roles & Permissions**? Those describe the app itself rather than the data it adds — they live under [Config](/l/ro/developers/extend/apps/config/overview). Looking for **Connections** (Linear, GitHub, Slack OAuth)? Those exist to be called *from* logic functions and live under [Logic](/l/ro/developers/extend/apps/logic/connections).
|
||||
Căutați **Application Config** sau **Roles & Permissions**? Acestea descriu aplicația în sine, mai degrabă decât datele pe care le adaugă — se află la [Config](/l/ro/developers/extend/apps/config/overview). Căutați **Connections** (Linear, GitHub, Slack OAuth)? Acestea există pentru a fi apelate *din* funcții de logică și se află la [Logic](/l/ro/developers/extend/apps/logic/connections).
|
||||
</Note>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Relații
|
||||
description: Connect objects together with bidirectional MANY_TO_ONE / ONE_TO_MANY relations.
|
||||
description: Conectați obiectele între ele cu relații bidirecționale MANY_TO_ONE / ONE_TO_MANY.
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
Relations connect two objects together. In Twenty, relations are always **bidirectional** — every relation has two sides, and each side is declared as a field that references the other.
|
||||
Relațiile conectează două obiecte între ele. În Twenty, relațiile sunt întotdeauna **bidirecționale** — fiecare relație are două părți, iar fiecare parte este declarată ca un câmp ce o referențiază pe cealaltă.
|
||||
|
||||
| Tip relație | Descriere | Are cheie străină? |
|
||||
| ------------- | ---------------------------------------------------------------------------------- | --------------------- |
|
||||
| `MANY_TO_ONE` | Multe înregistrări ale acestui obiect indică către o singură înregistrare a țintei | Da (`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | O înregistrare a acestui obiect are multe înregistrări ale țintei | No (the inverse side) |
|
||||
| `ONE_TO_MANY` | O înregistrare a acestui obiect are multe înregistrări ale țintei | Nu (partea inversă) |
|
||||
|
||||
## Cum funcționează relațiile
|
||||
|
||||
@@ -22,7 +22,7 @@ Ambele câmpuri folosesc `FieldType.RELATION` și se referențiază încrucișat
|
||||
|
||||
## Exemplu: Post Card are mulți destinatari
|
||||
|
||||
A `PostCard` can be sent to many `PostCardRecipient` records. Fiecare destinatar aparține exact unui Post Card.
|
||||
Un `PostCard` poate fi trimis către multe înregistrări `PostCardRecipient`. Fiecare destinatar aparține exact unui Post Card.
|
||||
|
||||
**Pasul 1: Definiți partea ONE_TO_MANY pe PostCard** (partea "one"):
|
||||
|
||||
@@ -81,7 +81,7 @@ export default defineField({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Circular imports:** both relation fields reference each other's `universalIdentifier`. To avoid circular import issues, export your field IDs as named constants from each file and import them in the other. Sistemul de build le rezolvă în timpul compilării.
|
||||
**Importuri circulare:** ambele câmpuri de relație se referă unul la celălalt prin `universalIdentifier`. Pentru a evita problemele de import circular, exportați ID-urile câmpurilor ca constante denumite din fiecare fișier și importați-le în celălalt. Sistemul de build le rezolvă în timpul compilării.
|
||||
</Note>
|
||||
|
||||
## Relaționarea cu obiectele standard
|
||||
@@ -124,16 +124,16 @@ export default defineField({
|
||||
|
||||
| Proprietate | Obligatoriu | Descriere |
|
||||
| ------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tip` | Da | Trebuie să fie `FieldType.RELATION` |
|
||||
| `type` | Da | Trebuie să fie `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Da | `universalIdentifier` al obiectului țintă |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Da | `universalIdentifier` al câmpului corespunzător de pe obiectul țintă |
|
||||
| `universalSettings.relationType` | Da | `RelationType.MANY_TO_ONE` sau `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | Doar MANY_TO_ONE | Ce se întâmplă atunci când înregistrarea referențiată este ștearsă: `CASCADE`, `SET_NULL`, `RESTRICT` sau `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | Doar MANY_TO_ONE | Numele coloanei din baza de date pentru cheia străină (de ex., `postCardId`) |
|
||||
|
||||
## Inline relation fields
|
||||
## Câmpuri de relație inline
|
||||
|
||||
You can also declare a relation directly inside [`defineObject`](/l/ro/developers/extend/apps/data/objects). When inline, omit `objectUniversalIdentifier` — it's inherited from the parent object:
|
||||
Puteți, de asemenea, să declarați o relație direct în [`defineObject`](/l/ro/developers/extend/apps/data/objects). Când este inline, omiteți `objectUniversalIdentifier` — este moștenit de la obiectul părinte:
|
||||
|
||||
```ts
|
||||
export default defineObject({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: How Twenty apps work — entity model, sandboxing, and the install lifecycle.
|
||||
title: Concepte
|
||||
description: Cum funcționează aplicațiile Twenty — modelul de entități, izolarea (sandboxing) și ciclul de viață al instalării.
|
||||
icon: sitemap
|
||||
---
|
||||
|
||||
@@ -34,22 +34,22 @@ your-app/
|
||||
|
||||
## Tipuri de entități
|
||||
|
||||
| Entitate | Scop | Documentație |
|
||||
| -------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| **Aplicație** | App identity, default role, variables | [Application Config](/l/ro/developers/extend/apps/config/application) |
|
||||
| **Rol** | Permission sets on objects and fields | [Roles & Permissions](/l/ro/developers/extend/apps/config/roles) |
|
||||
| **Obiect** | Custom record types with fields | [Objects](/l/ro/developers/extend/apps/data/objects) |
|
||||
| **Câmp** | Add fields to objects from other apps | [Extending Objects](/l/ro/developers/extend/apps/data/extending-objects) |
|
||||
| **Relation** | Bidirectional links between objects | [Relations](/l/ro/developers/extend/apps/data/relations) |
|
||||
| **Funcție logică** | TypeScript pe partea de server cu declanșatoare | [Funcții logice](/l/ro/developers/extend/apps/logic/logic-functions) |
|
||||
| **Abilitate** | Instrucțiuni reutilizabile pentru agenți AI | [Abilități și agenți](/l/ro/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | Agenți AI cu prompturi personalizate | [Abilități și agenți](/l/ro/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Connection Provider** | OAuth credentials for third-party APIs | [Connections](/l/ro/developers/extend/apps/logic/connections) |
|
||||
| **Vizualizare** | Vizualizări preconfigurate ale listelor de înregistrări | [Views](/l/ro/developers/extend/apps/layout/views) |
|
||||
| **Element de meniu de navigare** | Intrări personalizate în bara laterală | [Navigation Menu Items](/l/ro/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Layout pagină** | Tabs and widgets on a record's detail page | [Page Layouts](/l/ro/developers/extend/apps/layout/page-layouts) |
|
||||
| **Componentă front-end** | Sandboxed React UI inside Twenty | [Componente front-end](/l/ro/developers/extend/apps/layout/front-components) |
|
||||
| **Command Menu Item** | Quick actions and Cmd+K entries | [Command Menu Items](/l/ro/developers/extend/apps/layout/command-menu-items) |
|
||||
| Entitate | Scop | Documentație |
|
||||
| --------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| **Aplicație** | Identitatea aplicației, rolul implicit, variabile | [Application Config](/l/ro/developers/extend/apps/config/application) |
|
||||
| **Rol** | Seturi de permisiuni pentru obiecte și câmpuri | [Roles & Permissions](/l/ro/developers/extend/apps/config/roles) |
|
||||
| **Obiect** | Tipuri de înregistrări personalizate cu câmpuri | [Objects](/l/ro/developers/extend/apps/data/objects) |
|
||||
| **Câmp** | Adăugați câmpuri la obiecte din alte aplicații | [Extending Objects](/l/ro/developers/extend/apps/data/extending-objects) |
|
||||
| **Relație** | Legături bidirecționale între obiecte | [Relations](/l/ro/developers/extend/apps/data/relations) |
|
||||
| **Funcție logică** | TypeScript pe partea de server cu declanșatoare | [Funcții logice](/l/ro/developers/extend/apps/logic/logic-functions) |
|
||||
| **Abilitate** | Instrucțiuni reutilizabile pentru agenți AI | [Abilități și agenți](/l/ro/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | Agenți AI cu prompturi personalizate | [Abilități și agenți](/l/ro/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Furnizor de conexiune** | Date de autentificare OAuth pentru API-uri terțe | [Connections](/l/ro/developers/extend/apps/logic/connections) |
|
||||
| **Vizualizare** | Vizualizări preconfigurate ale listelor de înregistrări | [Views](/l/ro/developers/extend/apps/layout/views) |
|
||||
| **Element de meniu de navigare** | Intrări personalizate în bara laterală | [Navigation Menu Items](/l/ro/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Layout pagină** | File și widgeturi pe pagina de detalii a unei înregistrări | [Page Layouts](/l/ro/developers/extend/apps/layout/page-layouts) |
|
||||
| **Componentă front-end** | Interfață React izolată (sandboxed) în Twenty | [Componente front-end](/l/ro/developers/extend/apps/layout/front-components) |
|
||||
| **Element din meniul de comenzi** | Acțiuni rapide și intrări Cmd+K | [Command Menu Items](/l/ro/developers/extend/apps/layout/command-menu-items) |
|
||||
|
||||
## Izolare (sandboxing)
|
||||
|
||||
@@ -78,24 +78,24 @@ your-app/
|
||||
|
||||
* **`yarn twenty dev`** — monitorizează fișierele sursă și sincronizează în timp real modificările către un server Twenty conectat. Clientul API tipizat este regenerat automat atunci când schema se schimbă.
|
||||
* **`yarn twenty build`** — compilează TypeScript, împachetează funcțiile logice și componentele front-end cu esbuild și produce un manifest.
|
||||
* **Pre/post-install hooks** — optional functions that run during installation. See [Install Hooks](/l/ro/developers/extend/apps/config/install-hooks) for details.
|
||||
* **Hook-uri pre/post-instalare** — funcții opționale care rulează în timpul instalării. Vezi [Install Hooks](/l/ro/developers/extend/apps/config/install-hooks) pentru detalii.
|
||||
|
||||
## Pașii următori
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configurare" icon="screwdriver-wrench" href="/l/ro/developers/extend/apps/config/overview">
|
||||
Application identity, default role, and install hooks.
|
||||
Identitatea aplicației, rolul implicit și hook-urile de instalare.
|
||||
</Card>
|
||||
<Card title="Date" icon="database" href="/l/ro/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
Obiecte, câmpuri și relații bidirecționale.
|
||||
</Card>
|
||||
<Card title="Logică" icon="bolt" href="/l/ro/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
Funcții logice, abilități, agenți și conexiuni OAuth.
|
||||
</Card>
|
||||
<Card title="Aspect" icon="table-columns" href="/l/ro/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Vizualizări, navigare, layouturi de pagină, componente front-end.
|
||||
</Card>
|
||||
<Card title="Operațiuni" icon="rocket" href="/l/ro/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
CLI, testare, remote-uri, CI și publicarea aplicației.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Local Server
|
||||
description: Manage the local Twenty Docker server — start, stop, upgrade, parallel test instance, and manual SDK setup.
|
||||
title: Server local
|
||||
description: Gestionează serverul Twenty Docker local — pornește, oprește, actualizează, rulează o instanță de testare în paralel și configurează manual SDK-ul.
|
||||
icon: server
|
||||
---
|
||||
|
||||
@@ -39,11 +39,11 @@ Adăugați `--test` la orice comandă `server` pentru a gestiona o a doua instan
|
||||
| Comandă | Ce face |
|
||||
| ----------------------------------- | --------------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Pornește instanța de test (implicit pe portul 2021) |
|
||||
| `yarn twenty server stop --test` | Opriți-o |
|
||||
| `yarn twenty server status --test` | Afișați-i starea |
|
||||
| `yarn twenty server logs --test` | Transmiteți în flux jurnalele sale |
|
||||
| `yarn twenty server reset --test` | Ștergeți-i datele |
|
||||
| `yarn twenty server upgrade --test` | Actualizați-i imaginea |
|
||||
| `yarn twenty server stop --test` | Oprește-o |
|
||||
| `yarn twenty server status --test` | Afișează-i starea |
|
||||
| `yarn twenty server logs --test` | Transmite în flux jurnalele sale |
|
||||
| `yarn twenty server reset --test` | Șterge-i datele |
|
||||
| `yarn twenty server upgrade --test` | Actualizează-i imaginea |
|
||||
|
||||
Instanța de test are propriul container (`twenty-app-dev-test`), propriile volume (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) și propria configurație — rulează alături de instanța principală, fără conflicte. Combinați `--test` cu `--port` pentru a înlocui portul 2021.
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: What's inside a scaffolded Twenty app — files, folders, and what each one does.
|
||||
title: Structura proiectului
|
||||
description: Ce se află într-o aplicație Twenty generată cu scaffold — fișiere, foldere și ce face fiecare.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
A new app generated by `npx create-twenty-app` looks like this:
|
||||
O aplicație nouă generată de `npx create-twenty-app` arată astfel:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
@@ -36,5 +36,5 @@ my-twenty-app/
|
||||
| `public/` | Resurse statice (imagini, fonturi) servite împreună cu aplicația. |
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** The folders above are conventions — the SDK detects entities via AST analysis on `export default defineEntity(...)` calls regardless of where the file lives.
|
||||
**Organizarea fișierelor ține de dvs.** Folderele de mai sus sunt convenții — SDK-ul detectează entitățile prin analiză AST pe apelurile `export default defineEntity(...)`, indiferent unde se află fișierul.
|
||||
</Note>
|
||||
|
||||
@@ -123,7 +123,7 @@ Adăugați `--once` pentru a rula un singur build + sync și a ieși — acelaș
|
||||
yarn twenty dev --once
|
||||
```
|
||||
|
||||
| Comandă | Comportament | When to use |
|
||||
| Comandă | Comportament | Când se folosește |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
||||
| `yarn twenty dev` | Monitorizează și resincronizează la fiecare modificare. Rulează până când îl opriți. | Dezvoltare locală interactivă. |
|
||||
| `yarn twenty dev --once` | Un singur build + sync, iese cu `0` la succes, `1` la eșec. | CI, hook-uri pre-commit, agenți AI, fluxuri de lucru scriptate. |
|
||||
@@ -131,7 +131,7 @@ yarn twenty dev --once
|
||||
Ambele moduri necesită un server în modul de dezvoltare și un remote autentificat.
|
||||
|
||||
<Warning>
|
||||
Modul de dezvoltare este disponibil doar pe instanțele Twenty care rulează în modul development (`NODE_ENV=development`). Instanțele de producție resping cererile de sincronizare din modul de dezvoltare — folosiți `yarn twenty deploy` pentru a implementa pe serverele de producție. See [Publishing](/l/ro/developers/extend/apps/operations/publishing).
|
||||
Modul de dezvoltare este disponibil doar pe instanțele Twenty care rulează în modul development (`NODE_ENV=development`). Instanțele de producție resping cererile de sincronizare din modul de dezvoltare — folosiți `yarn twenty deploy` pentru a implementa pe serverele de producție. Consultați [Publicarea](/l/ro/developers/extend/apps/operations/publishing).
|
||||
</Warning>
|
||||
|
||||
---
|
||||
@@ -144,7 +144,7 @@ Folosiți `--example` pentru a începe cu un proiect mai complet (obiecte person
|
||||
npx create-twenty-app@latest my-twenty-app --example postcard
|
||||
```
|
||||
|
||||
Exemplele se află în [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). You can also scaffold individual entities into an existing project with `yarn twenty add` — see [Scaffolding](/l/ro/developers/extend/apps/getting-started/scaffolding).
|
||||
Exemplele se află în [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Puteți, de asemenea, să creați scheletul entităților individuale într-un proiect existent cu `yarn twenty add` — vedeți [Scaffolding](/l/ro/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
---
|
||||
|
||||
@@ -161,24 +161,24 @@ Aplicațiile sunt compuse din **entități** — fiecare definită într-un fiș
|
||||
| **Vizualizări și navigare** | Vizualizări de listă preconfigurate și elemente de meniu în bara laterală |
|
||||
| **Layouturi de pagină** | Pagini personalizate de detalii ale înregistrărilor cu file și widgeturi |
|
||||
|
||||
Full reference: [Concepts](/l/ro/developers/extend/apps/getting-started/concepts).
|
||||
Referință completă: [Concepte](/l/ro/developers/extend/apps/getting-started/concepts).
|
||||
|
||||
## Pașii următori
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configurare" icon="screwdriver-wrench" href="/l/ro/developers/extend/apps/config/overview">
|
||||
Application identity, default role, install hooks, public assets.
|
||||
Identitatea aplicației, rolul implicit, hook-urile de instalare, resursele publice.
|
||||
</Card>
|
||||
<Card title="Date" icon="database" href="/l/ro/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
Obiecte, câmpuri și relații bidirecționale.
|
||||
</Card>
|
||||
<Card title="Logică" icon="bolt" href="/l/ro/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
Funcții logice, abilități, agenți și conexiuni OAuth.
|
||||
</Card>
|
||||
<Card title="Aspect" icon="table-columns" href="/l/ro/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
Vizualizări, navigare, machete de pagină, componente front-end.
|
||||
</Card>
|
||||
<Card title="Operațiuni" icon="rocket" href="/l/ro/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
CLI, testare, remote-uri, CI și publicarea aplicației.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
title: Scaffolding
|
||||
description: Generate entity files interactively with yarn twenty add — objects, fields, views, logic functions, and more.
|
||||
title: Generare schelet (Scaffolding)
|
||||
description: Generați fișiere de entități în mod interactiv cu yarn twenty add — obiecte, câmpuri, vizualizări, funcții logice și altele.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Instead of creating entity files by hand, use the interactive scaffolder:
|
||||
În loc să creați manual fișiere de entități, folosiți generatorul interactiv (scaffolder):
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add
|
||||
```
|
||||
|
||||
It prompts you to pick an entity type and walks you through the required fields, then writes a ready-to-use file with a stable `universalIdentifier` and the correct `defineEntity()` call.
|
||||
Vă cere să alegeți un tip de entitate și vă ghidează prin câmpurile necesare, apoi scrie un fișier gata de utilizare cu un `universalIdentifier` stabil și apelul corect `defineEntity()`.
|
||||
|
||||
You can also pass the entity type directly to skip the first prompt:
|
||||
Puteți de asemenea să transmiteți direct tipul de entitate pentru a sări peste primul prompt:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add object
|
||||
@@ -20,38 +20,38 @@ yarn twenty add logicFunction
|
||||
yarn twenty add frontComponent
|
||||
```
|
||||
|
||||
## Available entity types
|
||||
## Tipuri de entități disponibile
|
||||
|
||||
| Entity type | Command | Generated file |
|
||||
| -------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Object | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Field | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Logic function | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Front component | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Role | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Skill | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| View | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Navigation menu item | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Page layout | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
| Tipul entității | Comandă | Fișier generat |
|
||||
| ---------------------------- | ------------------------------------ | ------------------------------------------------------- |
|
||||
| Obiect | `yarn twenty add object` | `src/objects/\<name>.ts` |
|
||||
| Câmp | `yarn twenty add field` | `src/fields/\<name>.ts` |
|
||||
| Funcție logică | `yarn twenty add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| Componentă frontend | `yarn twenty add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| Rol | `yarn twenty add role` | `src/roles/\<name>.ts` |
|
||||
| Abilitate | `yarn twenty add skill` | `src/skills/\<name>.ts` |
|
||||
| Agent | `yarn twenty add agent` | `src/agents/\<name>.ts` |
|
||||
| Vizualizare | `yarn twenty add view` | `src/views/\<name>.ts` |
|
||||
| Element de meniu de navigare | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| Machetă de pagină | `yarn twenty add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
|
||||
## What the scaffolder generates
|
||||
## Ce generează scaffolder-ul
|
||||
|
||||
Each entity type has its own template. For example, `yarn twenty add object` asks for:
|
||||
Fiecare tip de entitate are propriul său șablon. De exemplu, `yarn twenty add object` solicită:
|
||||
|
||||
1. **Name (singular)** — e.g., `invoice`
|
||||
2. **Name (plural)** — e.g., `invoices`
|
||||
3. **Label (singular)** — auto-populated from the name (e.g., `Invoice`)
|
||||
4. **Label (plural)** — auto-populated (e.g., `Invoices`)
|
||||
5. **Create a view and navigation item?** — if you answer yes, the scaffolder also generates a matching view and sidebar link for the new object.
|
||||
1. **Nume (singular)** — de ex., `invoice`
|
||||
2. **Nume (plural)** — de ex., `invoices`
|
||||
3. **Etichetă (singular)** — completată automat din nume (de ex., `Invoice`)
|
||||
4. **Etichetă (plural)** — completată automat (de ex., `Invoices`)
|
||||
5. **Creați o vizualizare și un element de navigare?** — dacă răspundeți afirmativ, scaffolder-ul generează, de asemenea, o vizualizare corespunzătoare și un link în bara laterală pentru noul obiect.
|
||||
|
||||
Other entity types have simpler prompts — most only ask for a name.
|
||||
Alte tipuri de entități au prompturi mai simple — majoritatea cer doar un nume.
|
||||
|
||||
The `field` entity type is more detailed: it asks for the field name, label, type (from a list of all available field types like `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.), and the target object's `universalIdentifier`.
|
||||
Tipul de entitate `field` este mai detaliat: solicită numele câmpului, eticheta, tipul (dintr-o listă cu toate tipurile de câmp disponibile precum `TEXT`, `NUMBER`, `SELECT`, `RELATION` etc.) și `universalIdentifier` al obiectului țintă.
|
||||
|
||||
## Custom output path
|
||||
## Cale de output personalizată
|
||||
|
||||
Use the `--path` flag to place the generated file in a custom location:
|
||||
Utilizați opțiunea `--path` pentru a plasa fișierul generat într-o locație personalizată:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty add logicFunction --path src/custom-folder
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Command Menu Items
|
||||
description: Surface front components as quick actions and command menu (Cmd+K) entries with defineCommandMenuItem.
|
||||
title: Elemente din meniul de comenzi
|
||||
description: Expune componente front-end ca acțiuni rapide și intrări în meniul de comenzi (Cmd+K) folosind defineCommandMenuItem.
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
A **command menu item** is the bridge between the user and a [front component](/l/ro/developers/extend/apps/layout/front-components). It registers the component in Twenty's command menu (Cmd+K) and, optionally, as a pinned quick-action button in the top-right corner of the page.
|
||||
Un element din meniul de comenzi este puntea dintre utilizator și un [front component](/l/ro/developers/extend/apps/layout/front-components). Acesta înregistrează componenta în meniul de comenzi Twenty (Cmd+K) și, opțional, ca buton fixat de acțiune rapidă în colțul din dreapta sus al paginii.
|
||||
|
||||
```ts src/command-menu-items/open-dashboard.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -20,25 +20,25 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration fields
|
||||
## Câmpuri de configurare
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Yes | Stable unique ID for the command |
|
||||
| `label` | Yes | Full label shown in the command menu (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Yes | The `universalIdentifier` of the front component this command opens |
|
||||
| `shortLabel` | No | Shorter label displayed on the pinned quick-action button |
|
||||
| `icon` | No | Icon name displayed next to the label (e.g. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | No | When `true`, shows the command as a quick-action button in the top-right corner of the page |
|
||||
| `availabilityType` | No | Controls where the command appears: `'GLOBAL'` (always available), `'RECORD_SELECTION'` (only when records are selected), or `'FALLBACK'` (shown when no other commands match) |
|
||||
| `availabilityObjectUniversalIdentifier` | No | Restrict the command to pages of a specific object type (e.g. only on Company records) |
|
||||
| `conditionalAvailabilityExpression` | No | A boolean expression that dynamically controls visibility (see below) |
|
||||
| Câmp | Obligatoriu | Descriere |
|
||||
| --------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | Da | ID unic stabil pentru comandă |
|
||||
| `label` | Da | Etichetă completă afișată în meniul de comenzi (Cmd+K) |
|
||||
| `frontComponentUniversalIdentifier` | Da | `universalIdentifier` al componentei front-end pe care această comandă o deschide |
|
||||
| `shortLabel` | Nu | Etichetă mai scurtă afișată pe butonul de acțiune rapidă fixat |
|
||||
| `icon` | Nu | Numele pictogramei afișat lângă etichetă (de ex. `'IconBolt'`, `'IconSend'`) |
|
||||
| `isPinned` | Nu | Când este `true`, afișează comanda ca buton de acțiune rapidă în colțul din dreapta sus al paginii |
|
||||
| `availabilityType` | Nu | Controlează unde apare comanda: `'GLOBAL'` (mereu disponibilă), `'RECORD_SELECTION'` (doar când sunt selectate înregistrări) sau `'FALLBACK'` (afișată când nicio altă comandă nu se potrivește) |
|
||||
| `availabilityObjectUniversalIdentifier` | Nu | Restricționați comanda la paginile unui anumit tip de obiect (de ex., doar pe înregistrările Company) |
|
||||
| `conditionalAvailabilityExpression` | Nu | O expresie booleană care controlează dinamic vizibilitatea (vezi mai jos) |
|
||||
|
||||
## Headless commands
|
||||
## Comenzi headless
|
||||
|
||||
A command menu item paired with a [headless front component](/l/ro/developers/extend/apps/layout/front-components#headless-vs-non-headless) is the idiomatic way to ship a one-click action — run code, navigate, or confirm and execute. The Front Components page covers the [SDK Command components](/l/ro/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) that handle the action-and-unmount pattern.
|
||||
Un element din meniul de comenzi asociat cu un [headless front component](/l/ro/developers/extend/apps/layout/front-components#headless-vs-non-headless) este modalitatea standard de a oferi o acțiune cu un singur clic — de a rula cod, de a naviga sau de a confirma și executa. Pagina Front Components acoperă [SDK Command components](/l/ro/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) care gestionează modelul acțiune-și-demontare.
|
||||
|
||||
A typical flow:
|
||||
Un flux tipic:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -79,9 +79,9 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
## Conditional availability expressions
|
||||
## Expresii de disponibilitate condițională
|
||||
|
||||
The `conditionalAvailabilityExpression` field lets you control when a command is visible based on the current page context. Import typed variables and operators from `twenty-sdk` to build expressions:
|
||||
Câmpul `conditionalAvailabilityExpression` vă permite să controlați când este vizibilă o comandă în funcție de contextul paginii curente. Importați variabile tipizate și operatori din `twenty-sdk` pentru a construi expresii:
|
||||
|
||||
```ts src/command-menu-items/bulk-update.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
@@ -103,42 +103,42 @@ export default defineCommandMenuItem({
|
||||
});
|
||||
```
|
||||
|
||||
### Context variables
|
||||
### Variabile de context
|
||||
|
||||
These represent the current state of the page:
|
||||
Acestea reprezintă starea curentă a paginii:
|
||||
|
||||
| Variable | Type | Description |
|
||||
| ------------------------------ | --------- | ---------------------------------------------------------------- |
|
||||
| `pageType` | `string` | Current page type (e.g. `'RecordIndexPage'`, `'RecordShowPage'`) |
|
||||
| `isInSidePanel` | `boolean` | Whether the component is rendered in a side panel |
|
||||
| `numberOfSelectedRecords` | `number` | Number of currently selected records |
|
||||
| `isSelectAll` | `boolean` | Whether "select all" is active |
|
||||
| `selectedRecords` | `array` | The selected record objects |
|
||||
| `favoriteRecordIds` | `array` | IDs of favorited records |
|
||||
| `objectPermissions` | `object` | Permissions for the current object type |
|
||||
| `targetObjectReadPermissions` | `object` | Read permissions for the target object |
|
||||
| `targetObjectWritePermissions` | `object` | Write permissions for the target object |
|
||||
| `featureFlags` | `object` | Active feature flags |
|
||||
| `objectMetadataItem` | `object` | Metadata of the current object type |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Whether the current view has a soft-delete filter |
|
||||
| Variabilă | Tip | Descriere |
|
||||
| ------------------------------ | --------- | ---------------------------------------------------------------------- |
|
||||
| `pageType` | `string` | Tipul paginii curente (de ex. `'RecordIndexPage'`, `'RecordShowPage'`) |
|
||||
| `isInSidePanel` | `boolean` | Dacă componenta este redată într-un panou lateral |
|
||||
| `numberOfSelectedRecords` | `number` | Numărul de înregistrări selectate în prezent |
|
||||
| `isSelectAll` | `boolean` | Dacă "select all" este activ |
|
||||
| `selectedRecords` | `array` | Obiectele înregistrărilor selectate |
|
||||
| `favoriteRecordIds` | `array` | ID-urile înregistrărilor marcate ca favorite |
|
||||
| `objectPermissions` | `object` | Permisiuni pentru tipul de obiect curent |
|
||||
| `targetObjectReadPermissions` | `object` | Permisiuni de citire pentru obiectul țintă |
|
||||
| `targetObjectWritePermissions` | `object` | Permisiuni de scriere pentru obiectul țintă |
|
||||
| `featureFlags` | `object` | Steaguri de caracteristici active |
|
||||
| `objectMetadataItem` | `object` | Metadatele tipului de obiect curent |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | Dacă vizualizarea curentă are un filtru soft-delete |
|
||||
|
||||
### Operators
|
||||
### Operatori
|
||||
|
||||
Combine variables into boolean expressions:
|
||||
Combinați variabilele în expresii booleene:
|
||||
|
||||
| Operator | Description |
|
||||
| ----------------------------------- | ----------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true` if the value is not null/undefined |
|
||||
| `isNonEmptyString(value)` | `true` if the value is a non-empty string |
|
||||
| `includes(array, value)` | `true` if the array contains the value |
|
||||
| `includesEvery(array, prop, value)` | `true` if every item's property includes the value |
|
||||
| `every(array, prop)` | `true` if the property is truthy on every item |
|
||||
| `everyDefined(array, prop)` | `true` if the property is defined on every item |
|
||||
| `everyEquals(array, prop, value)` | `true` if the property equals the value on every item |
|
||||
| `some(array, prop)` | `true` if the property is truthy on at least one item |
|
||||
| `someDefined(array, prop)` | `true` if the property is defined on at least one item |
|
||||
| `someEquals(array, prop, value)` | `true` if the property equals the value on at least one item |
|
||||
| `someNonEmptyString(array, prop)` | `true` if the property is a non-empty string on at least one item |
|
||||
| `none(array, prop)` | `true` if the property is falsy on every item |
|
||||
| `noneDefined(array, prop)` | `true` if the property is undefined on every item |
|
||||
| `noneEquals(array, prop, value)` | `true` if the property does not equal the value on any item |
|
||||
| Operator | Descriere |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `isDefined(value)` | `true` dacă valoarea nu este null/undefined |
|
||||
| `isNonEmptyString(value)` | `true` dacă valoarea este un șir nevid |
|
||||
| `includes(array, value)` | `true` dacă array-ul conține valoarea |
|
||||
| `includesEvery(array, prop, value)` | `true` dacă proprietatea fiecărui element include valoarea |
|
||||
| `every(array, prop)` | `true` dacă proprietatea este truthy pentru fiecare element |
|
||||
| `everyDefined(array, prop)` | `true` dacă proprietatea este definită pentru fiecare element |
|
||||
| `everyEquals(array, prop, value)` | `true` dacă proprietatea este egală cu valoarea pentru fiecare element |
|
||||
| `some(array, prop)` | `true` dacă proprietatea este truthy pe cel puțin un element |
|
||||
| `someDefined(array, prop)` | `true` dacă proprietatea este definită pe cel puțin un element |
|
||||
| `someEquals(array, prop, value)` | `true` dacă proprietatea este egală cu valoarea pe cel puțin un element |
|
||||
| `someNonEmptyString(array, prop)` | `true` dacă proprietatea este un șir nevid pe cel puțin un element |
|
||||
| `none(array, prop)` | `true` dacă proprietatea este falsy pentru fiecare element |
|
||||
| `noneDefined(array, prop)` | `true` dacă proprietatea este nedefinită pentru fiecare element |
|
||||
| `noneEquals(array, prop, value)` | `true` dacă proprietatea nu este egală cu valoarea pe niciun element |
|
||||
|
||||
@@ -6,21 +6,21 @@ icon: window-maximize
|
||||
|
||||
Componentele front-end sunt componente React care se afișează direct în interfața Twenty. Rulează într-un **Web Worker** izolat folosind Remote DOM — codul este izolat (sandboxed), dar se redă nativ în pagină, nu într-un iframe.
|
||||
|
||||
## Unde pot fi utilizate componentele frontale
|
||||
## Unde pot fi utilizate componentele front-end
|
||||
|
||||
Componentele frontale pot fi afișate în două locații în cadrul Twenty:
|
||||
Componentele front-end pot fi afișate în două locații în cadrul Twenty:
|
||||
|
||||
* **Panou lateral** — Componentele frontale care nu sunt headless se deschid în panoul lateral din dreapta. Acesta este comportamentul implicit atunci când o componentă frontală este declanșată din meniul de comenzi.
|
||||
* **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside [page layouts](/l/ro/developers/extend/apps/layout/page-layouts). La configurarea unui tablou de bord sau a machetei unei pagini de înregistrare, utilizatorii pot adăuga un widget de componentă frontală.
|
||||
* **Panou lateral** — Componentele front-end care nu sunt headless se deschid în panoul lateral din dreapta. Acesta este comportamentul implicit atunci când o componentă front-end este declanșată din meniul de comenzi.
|
||||
* **Widgeturi (tablouri de bord și pagini de înregistrare)** — Componentele frontale pot fi încorporate ca widgeturi în [machetele de pagină](/l/ro/developers/extend/apps/layout/page-layouts). La configurarea unui tablou de bord sau a machetei unei pagini de înregistrare, utilizatorii pot adăuga un widget de componentă front-end.
|
||||
|
||||
A front component on its own isn't reachable from the UI — you need to *surface* it. The two ways to do that are:
|
||||
Un front component de unul singur nu este accesibil din interfața utilizator — trebuie să îl *expui*. Cele două moduri de a face asta sunt:
|
||||
|
||||
* **Pair it with a [command menu item](/l/ro/developers/extend/apps/layout/command-menu-items)** — registers it in the command menu (Cmd+K) and, optionally, as a pinned quick-action.
|
||||
* **Embed it as a widget in a [page layout](/l/ro/developers/extend/apps/layout/page-layouts)** — places it on a record's detail page or dashboard.
|
||||
* **Asociază-l cu un [element de meniu de comenzi](/l/ro/developers/extend/apps/layout/command-menu-items)** — îl înregistrează în meniul de comenzi (Cmd+K) și, opțional, ca acțiune rapidă fixată.
|
||||
* **Încorporează-l ca widget într-o [machetă de pagină](/l/ro/developers/extend/apps/layout/page-layouts)** — îl plasează pe pagina de detalii a unei înregistrări sau pe un tablou de bord.
|
||||
|
||||
## Exemplu de bază
|
||||
|
||||
The quickest way to see a front component in action is to pair it with a [`defineCommandMenuItem`](/l/ro/developers/extend/apps/layout/command-menu-items), so it appears as a quick-action button in the top-right corner of the page:
|
||||
Cel mai rapid mod de a vedea un front component în acțiune este să îl asociezi cu un [`defineCommandMenuItem`](/l/ro/developers/extend/apps/layout/command-menu-items), astfel încât să apară ca un buton de acțiune rapidă în colțul din dreapta sus al paginii:
|
||||
|
||||
```tsx src/front-components/hello-world.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
@@ -76,13 +76,13 @@ Faceți clic pe el pentru a afișa componenta inline.
|
||||
|
||||
## Plasarea unei componente front-end pe o pagină
|
||||
|
||||
Dincolo de comenzi, puteți încorpora o componentă front-end direct într-o pagină de înregistrare adăugând-o ca widget într-un **layout de pagină**. See [Page Layouts](/l/ro/developers/extend/apps/layout/page-layouts) for details.
|
||||
Dincolo de comenzi, puteți încorpora o componentă front-end direct într-o pagină de înregistrare adăugând-o ca widget într-un **layout de pagină**. Vezi [Machete de pagină](/l/ro/developers/extend/apps/layout/page-layouts) pentru detalii.
|
||||
|
||||
## Headless vs non-headless
|
||||
|
||||
Componentele frontale au două moduri de randare controlate de opțiunea `isHeadless`:
|
||||
Componentele front-end au două moduri de randare controlate de opțiunea `isHeadless`:
|
||||
|
||||
**Non-headless (implicit)** — Componenta afișează o interfață vizibilă. Când este declanșat din meniul de comenzi, se deschide în panoul lateral. Acesta este comportamentul implicit când `isHeadless` este `false` sau omis.
|
||||
**Non-headless (implicit)** — Componenta afișează o interfață vizibilă. Când este declanșată din meniul de comenzi, se deschide în panoul lateral. Acesta este comportamentul implicit când `isHeadless` este `false` sau omis.
|
||||
|
||||
**Headless (`isHeadless: true`)** — Componenta se montează invizibil în fundal. Nu deschide panoul lateral. Componentele headless sunt concepute pentru acțiuni care execută logică și apoi se demontează — de exemplu, rularea unei sarcini asincrone, navigarea la o pagină sau afișarea unui modal de confirmare. Se potrivesc în mod natural cu componentele Command din SDK descrise mai jos.
|
||||
|
||||
@@ -114,9 +114,9 @@ Deoarece componenta returnează `null`, Twenty omite redarea unui container pent
|
||||
|
||||
## Componentele Command din SDK
|
||||
|
||||
Pachetul `twenty-sdk` oferă patru componente ajutătoare Command, concepute pentru componente front-end headless. Fiecare componentă execută o acțiune la montare, gestionează erorile afișând o notificare snackbar și demontează automat componenta de interfață la final.
|
||||
Pachetul `twenty-sdk` oferă patru componente ajutătoare Command, concepute pentru componente front-end headless. Fiecare componentă execută o acțiune la montare, gestionează erorile afișând o notificare snackbar și demontează automat componenta front-end la final.
|
||||
|
||||
Importă-le din `twenty-sdk/command`:
|
||||
Importați-le din `twenty-sdk/command`:
|
||||
|
||||
* **`Command`** — Rulează un callback asincron prin prop-ul `execute`.
|
||||
* **`CommandLink`** — Navighează către o rută a aplicației. Props: `to`, `params`, `queryParams`, `options`.
|
||||
@@ -253,7 +253,7 @@ Componentele front-end pot declanșa navigare, ferestre modale și notificări f
|
||||
| `unmountFrontComponent()` | Demontați componenta |
|
||||
| `updateProgress(progress)` | Actualizați un indicator de progres |
|
||||
|
||||
Iată un exemplu care folosește API-ul gazdă pentru a afișa un snackbar și a închide panoul lateral după finalizarea unei acțiuni:
|
||||
Iată un exemplu care folosește API-ul gazdei pentru a afișa un snackbar și a închide panoul lateral după finalizarea unei acțiuni:
|
||||
|
||||
```tsx src/front-components/archive-record.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Navigation Menu Items
|
||||
description: Add custom entries to the workspace sidebar — links to saved views or external URLs.
|
||||
title: Elemente de meniu de navigare
|
||||
description: Adăugați intrări personalizate în bara laterală a spațiului de lucru — linkuri către vizualizări salvate sau URL-uri externe.
|
||||
icon: bars
|
||||
---
|
||||
|
||||
A **navigation menu item** is an entry in the left sidebar. Use `defineNavigationMenuItem()` to ship custom sidebar links — typically one per [view](/l/ro/developers/extend/apps/layout/views) you ship — or to point at external URLs.
|
||||
Un **element de meniu de navigare** este o intrare în bara laterală din stânga. Folosiți `defineNavigationMenuItem()` pentru a livra linkuri personalizate în bara laterală — de obicei unul pentru fiecare [vizualizare](/l/ro/developers/extend/apps/layout/views) pe care o livrați — sau pentru a indica către URL-uri externe.
|
||||
|
||||
```ts src/navigation-menu-items/example-navigation-menu-item.ts
|
||||
import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define';
|
||||
@@ -23,22 +23,22 @@ export default defineNavigationMenuItem({
|
||||
|
||||
## Puncte cheie
|
||||
|
||||
* `type` determines what the menu item links to. Each type pairs with a specific identifier field:
|
||||
* `type` determină la ce face legătura elementul de meniu. Fiecare tip este asociat cu un câmp identificator specific:
|
||||
|
||||
| Tip | Ce face | Required field |
|
||||
| ------------------------------------ | ------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | Opens a saved view | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Opens an external URL | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Groups nested items under a label | `name` (and child items reference the folder via `folderUniversalIdentifier`) |
|
||||
| `NavigationMenuItemType.OBJECT` | Opens an object's default index page | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Opens a standalone page layout | `pageLayoutUniversalIdentifier` |
|
||||
| Tip | Ce face | Câmp obligatoriu |
|
||||
| ------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
|
||||
| `NavigationMenuItemType.VIEW` | Deschide o vizualizare salvată | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | Deschide un URL extern | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | Grupează elementele îmbricate sub o etichetă | `name` (iar elementele copil fac referire la dosar prin `folderUniversalIdentifier`) |
|
||||
| `NavigationMenuItemType.OBJECT` | Deschide pagina implicită de index a unui obiect | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | Deschide un aranjament de pagină independent | `pageLayoutUniversalIdentifier` |
|
||||
|
||||
* `position` controls ordering in the sidebar.
|
||||
* `position` controlează ordonarea în bara laterală.
|
||||
|
||||
* `icon` and `color` are optional and customize how the entry looks.
|
||||
* `icon` și `color` sunt opționale și personalizează aspectul intrării.
|
||||
|
||||
* `folderUniversalIdentifier` is also available on any item to nest it inside a `FOLDER`-type parent.
|
||||
* `folderUniversalIdentifier` este de asemenea disponibil pe orice element pentru a-l îmbrica într-un părinte de tip `FOLDER`.
|
||||
|
||||
<Note>
|
||||
**Common pitfall:** creating an object without an associated view + navigation menu item makes that object invisible to users. Unless it's a technical/internal object, every custom object should have a default view *and* a sidebar entry pointing at it.
|
||||
**Capcană frecventă:** crearea unui obiect fără o vizualizare asociată + element de meniu de navigare face ca acel obiect să fie invizibil pentru utilizatori. Dacă nu este un obiect tehnic/intern, fiecare obiect personalizat ar trebui să aibă o vizualizare implicită *și* o intrare în bara laterală care să indice către aceasta.
|
||||
</Note>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Place your app inside Twenty's UI — sidebar entries, saved views, record page tabs, and sandboxed React components.
|
||||
title: Prezentare generală
|
||||
description: Integrează-ți aplicația în interfața Twenty — intrări în bara laterală, vizualizări salvate, file în pagina de detalii a înregistrărilor și componente React izolate.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A Twenty app's **layout layer** is everything the user sees: where the app surfaces in the sidebar, which list views it ships, how its record detail pages are arranged, and which custom React components render inside those pages.
|
||||
**Stratul de layout** al unei aplicații Twenty reprezintă tot ceea ce vede utilizatorul: unde apare aplicația în bara laterală, ce vizualizări de listă oferă, cum sunt aranjate paginile de detalii ale înregistrărilor și ce componente React personalizate sunt afișate în interiorul acelor pagini.
|
||||
|
||||
```text
|
||||
Sidebar Record list Record detail page
|
||||
@@ -23,34 +23,34 @@ A Twenty app's **layout layer** is everything the user sees: where the app surfa
|
||||
and filters └─────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## În această secțiune
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Views" icon="list" href="/l/ro/developers/extend/apps/layout/views">
|
||||
`defineView` — saved list configurations: visible columns, filters, groups.
|
||||
<Card title="Vizualizări" icon="listă" href="/l/ro/developers/extend/apps/layout/views">
|
||||
`defineView` — configurații salvate de liste: coloane vizibile, filtre, grupuri.
|
||||
</Card>
|
||||
<Card title="Navigation Menu Items" icon="bars" href="/l/ro/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — sidebar entries pointing at views or external URLs.
|
||||
<Card title="Elemente din meniul de navigare" icon="bars" href="/l/ro/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — intrări în bara laterală care trimit către vizualizări sau URL-uri externe.
|
||||
</Card>
|
||||
<Card title="Page Layouts" icon="table-columns" href="/l/ro/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` and `definePageLayoutTab` — tabs and widgets on a record's detail page.
|
||||
<Card title="Layouturi de pagină" icon="table-columns" href="/l/ro/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` și `definePageLayoutTab` — file și widgeturi pe pagina de detalii a unei înregistrări.
|
||||
</Card>
|
||||
<Card title="Front Components" icon="window-maximize" href="/l/ro/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — sandboxed React components that render inside Twenty.
|
||||
<Card title="Componente front-end" icon="window-maximize" href="/l/ro/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — componente React izolate care rulează în interiorul Twenty.
|
||||
</Card>
|
||||
<Card title="Command Menu Items" icon="terminal" href="/l/ro/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — register front components as Cmd+K entries and quick actions.
|
||||
<Card title="Elemente din meniul de comenzi" icon="terminal" href="/l/ro/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — înregistrează componente front ca intrări Cmd+K și acțiuni rapide.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Where the app surfaces
|
||||
## Unde este afișată aplicația
|
||||
|
||||
| Surface | What it controls | Entity |
|
||||
| --------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Sidebar** | A custom entry linking to a saved view or external URL | `defineNavigationMenuItem` |
|
||||
| **Record list** | A saved configuration for an object — visible columns, order, filters, groups | `defineView` |
|
||||
| **Record detail page** | The tabs and widgets on a record page (your own object's, or a standard one) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **Inside any of the above** | A custom React widget — buttons, forms, dashboards, integrations | `defineFrontComponent` |
|
||||
| **Command menu (Cmd+K)** | A pinned quick action or hidden command | `defineCommandMenuItem` |
|
||||
| Suprafață | Ce controlează | Entitate |
|
||||
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Bară laterală** | O intrare personalizată care face legătura către o vizualizare salvată sau un URL extern | `defineNavigationMenuItem` |
|
||||
| **Listă de înregistrări** | O configurație salvată pentru un obiect — coloane vizibile, ordine, filtre, grupuri | `defineView` |
|
||||
| **Pagină de detalii a înregistrării** | Filele și widgeturile de pe o pagină de înregistrare (ale propriului tău obiect sau ale unuia standard) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **În interiorul oricăreia dintre cele de mai sus** | Un widget React personalizat — butoane, formulare, dashboarduri, integrări | `defineFrontComponent` |
|
||||
| **Meniul de comenzi (Cmd+K)** | O acțiune rapidă fixată sau o comandă ascunsă | `defineCommandMenuItem` |
|
||||
|
||||
Front components run inside an isolated Web Worker using Remote DOM — they render *natively* in the page (not inside an iframe), but cannot reach the host page or DOM directly. Communication with Twenty happens through a message-passing host API.
|
||||
Componentele front rulează în interiorul unui Web Worker izolat folosind Remote DOM — acestea sunt redate *nativ* în pagină (nu într-un iframe), dar nu pot accesa direct pagina gazdă sau DOM-ul. Comunicarea cu Twenty se face printr-un API al gazdei bazat pe transmiterea de mesaje.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Layouturi de pagină
|
||||
description: Customize record detail pages — tabs, widgets, and where front components render — using definePageLayout and definePageLayoutTab.
|
||||
description: Personalizează paginile de detaliu ale înregistrărilor — filele, widgeturile și locul în care se redau componentele front-end — folosind definePageLayout și definePageLayoutTab.
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
A **page layout** controls how a record's detail page is arranged: which tabs appear and what widgets they contain. Use `definePageLayout()` to declare a layout for an object you own, or `definePageLayoutTab()` to add a single tab to a layout that already exists (yours or a standard Twenty one).
|
||||
Un **layout de pagină** controlează modul în care este aranjată pagina de detalii a unei înregistrări: ce file apar și ce widgeturi conțin acestea. Folosește `definePageLayout()` pentru a declara un layout pentru un obiect pe care îl deții sau `definePageLayoutTab()` pentru a adăuga o singură filă la un layout care există deja (al tău sau un layout standard Twenty).
|
||||
|
||||
| Use case | Entitate |
|
||||
| ---------------------------------------------------------------------- | --------------------- |
|
||||
| Define the entire layout for a record page on an object you own | `definePageLayout` |
|
||||
| Add one tab to an existing layout (your own object, or a standard one) | `definePageLayoutTab` |
|
||||
| Caz de utilizare | Entitate |
|
||||
| --------------------------------------------------------------------------------------- | --------------------- |
|
||||
| Definește întregul layout pentru o pagină de înregistrare pe un obiect pe care îl deții | `definePageLayout` |
|
||||
| Adaugă o filă la un layout existent (obiectul tău sau unul standard) | `definePageLayoutTab` |
|
||||
|
||||
## definePageLayout
|
||||
|
||||
Use this when you own the entire detail page — typically for a custom object you defined yourself.
|
||||
Folosește aceasta atunci când deții întreaga pagină de detalii — de obicei pentru un obiect personalizat pe care l-ai definit chiar tu.
|
||||
|
||||
```ts src/page-layouts/example-record-page-layout.ts
|
||||
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
|
||||
@@ -54,12 +54,12 @@ export default definePageLayout({
|
||||
* `type` este de obicei `'RECORD_PAGE'` pentru a personaliza vizualizarea de detaliu a unui obiect specific.
|
||||
* `objectUniversalIdentifier` specifică la ce obiect se aplică această machetă.
|
||||
* Fiecare `tab` definește o secțiune a paginii cu un `title`, `position` și `layoutMode` (`CANVAS` pentru layout liber).
|
||||
* Each `widget` inside a tab can render a [front component](/l/ro/developers/extend/apps/layout/front-components), a relation list, or other built-in widget types.
|
||||
* `position` pe file le controlează ordinea. Folosiți valori mai mari (de ex., 50) pentru a plasa filele personalizate după cele integrate.
|
||||
* Fiecare `widget` dintr-o filă poate reda un [front component](/l/ro/developers/extend/apps/layout/front-components), o listă de relații sau alte tipuri de widgeturi integrate.
|
||||
* `position` pe file le controlează ordinea. Folosește valori mai mari (de ex., 50) pentru a plasa filele personalizate după cele integrate.
|
||||
|
||||
## definePageLayoutTab
|
||||
|
||||
Use this when you only want to **add** a tab to an existing layout — for example, an analytics tab on the standard Company page, or an AI summary tab attached to your own object's layout.
|
||||
Folosește aceasta atunci când vrei doar să **adaugi** o filă la un layout existent — de exemplu, o filă de analize pe pagina standard Company sau o filă de rezumat AI atașată layoutului obiectului tău.
|
||||
|
||||
```ts src/page-layouts/example-extra-tab.ts
|
||||
import {
|
||||
@@ -96,7 +96,7 @@ export default definePageLayoutTab({
|
||||
|
||||
### Puncte cheie
|
||||
|
||||
* `pageLayoutUniversalIdentifier` is **required** and must point to a page layout that already exists at install time — either a standard Twenty layout or one defined by your own app. Cross-app references to layouts owned by another installed app are not supported today. When the parent layout is missing, installation fails with a clear validation error.
|
||||
* `widgets` are scoped to this tab only — they reference [front components](/l/ro/developers/extend/apps/layout/front-components), views, etc. exactly like widgets defined inline in `definePageLayout`.
|
||||
* `pageLayoutUniversalIdentifier` este **obligatoriu** și trebuie să indice către un layout de pagină care există deja la momentul instalării — fie un layout standard Twenty, fie unul definit de propria ta aplicație. Referințele cross-app către layouturi deținute de o altă aplicație instalată nu sunt acceptate în prezent. Când lipsește layoutul părinte, instalarea eșuează cu o eroare clară de validare.
|
||||
* `widgets` sunt limitate doar la această filă — fac referire la [front components](/l/ro/developers/extend/apps/layout/front-components), vizualizări etc., exact ca widgeturile definite inline în `definePageLayout`.
|
||||
* `position` controlează ordonarea în raport cu filele existente din layoutul țintă. Alege o valoare care să plaseze fila ta acolo unde dorești, relativ la filele predefinite.
|
||||
* Use this instead of `definePageLayout` when you only want to add to an existing layout. Use `definePageLayout` when you own the entire layout.
|
||||
* Folosește aceasta în loc de `definePageLayout` atunci când vrei doar să adaugi la un layout existent. Folosește `definePageLayout` atunci când deții întregul layout.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Vizualizări
|
||||
description: Ship pre-configured saved views — column order, filters, groups — for objects in your app.
|
||||
icon: list
|
||||
description: Livrați vizualizări salvate preconfigurate — ordinea coloanelor, filtre, grupări — pentru obiectele din aplicația dvs.
|
||||
icon: listă
|
||||
---
|
||||
|
||||
A **view** is a saved configuration for how records of an object are displayed: which fields appear, their order, whether they're visible, and any filters or groups applied. Use `defineView()` to ship pre-configured views with your app — typically a default index view for each custom object you create.
|
||||
O **vizualizare** este o configurație salvată pentru modul în care sunt afișate înregistrările unui obiect: ce câmpuri apar, ordinea lor, dacă sunt vizibile și ce filtre sau grupări sunt aplicate. Folosiți `defineView()` pentru a livra vizualizări preconfigurate împreună cu aplicația dvs. — de obicei o vizualizare index implicită pentru fiecare obiect personalizat pe care îl creați.
|
||||
|
||||
```ts src/views/example-view.ts
|
||||
import { defineView, ViewKey } from 'twenty-sdk/define';
|
||||
@@ -32,12 +32,12 @@ export default defineView({
|
||||
|
||||
## Puncte cheie
|
||||
|
||||
* `objectUniversalIdentifier` specifică la ce obiect se aplică această vizualizare. It can be a custom object you defined or a standard Twenty object.
|
||||
* `key` determines the view type — `ViewKey.INDEX` is the main list view for the object.
|
||||
* `fields` controls which columns appear and in what order. Fiecare câmp face referire la un `fieldMetadataUniversalIdentifier`.
|
||||
* You can also declare `filters`, `filterGroups`, `groups`, and `fieldGroups` for advanced configurations.
|
||||
* `position` controls ordering when multiple views exist for the same object.
|
||||
* `objectUniversalIdentifier` specifică la ce obiect se aplică această vizualizare. Poate fi un obiect personalizat pe care l-ați definit sau un obiect standard Twenty.
|
||||
* `key` determină tipul vizualizării — `ViewKey.INDEX` este principala vizualizare de listă pentru obiect.
|
||||
* `fields` controlează ce coloane apar și ordinea acestora. Fiecare câmp face referire la un `fieldMetadataUniversalIdentifier`.
|
||||
* Puteți declara, de asemenea, `filters`, `filterGroups`, `groups` și `fieldGroups` pentru configurații avansate.
|
||||
* `position` controlează ordonarea atunci când există mai multe vizualizări pentru același obiect.
|
||||
|
||||
## How views show up in the UI
|
||||
## Cum apar vizualizările în interfața utilizator
|
||||
|
||||
A view by itself isn't reachable from the sidebar. To make it appear there, pair it with a [navigation menu item](/l/ro/developers/extend/apps/layout/navigation-menu-items) of type `VIEW` that points at the view's `universalIdentifier`. That's the canonical pattern: every custom object typically ships a default view + a sidebar entry that opens it.
|
||||
O vizualizare, de una singură, nu este accesibilă din bara laterală. Pentru a o face să apară acolo, asociați-o cu un [element de meniu de navigare](/l/ro/developers/extend/apps/layout/navigation-menu-items) de tip `VIEW` care indică către `universalIdentifier` al vizualizării. Acesta este modelul canonic: fiecare obiect personalizat livrează, de obicei, o vizualizare implicită + o intrare în bara laterală care o deschide.
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
title: Connections
|
||||
description: Let your app act on a user's behalf in third-party services via OAuth.
|
||||
title: Conexiuni
|
||||
description: Permite aplicației tale să acționeze în numele unui utilizator în servicii ale terților prin OAuth.
|
||||
icon: plug
|
||||
---
|
||||
|
||||
Connections are credentials a user holds for an external service (Linear, GitHub, Slack, ...). Your app declares **how** those credentials are obtained — a **connection provider** — and consumes them at runtime to make authenticated calls to the third-party API.
|
||||
Conexiunile sunt acreditări pe care un utilizator le deține pentru un serviciu extern (Linear, GitHub, Slack, ...). Aplicația ta declară **cum** sunt obținute acele acreditări — un **furnizor de conexiune** — și le folosește în timpul execuției pentru a efectua apeluri autentificate către API-ul terț.
|
||||
|
||||
Today only OAuth 2.0 is supported. Future credential types (personal access tokens, API keys, basic auth) will plug into the same surface — apps already using `defineConnectionProvider({ type: 'oauth', ... })` won't need to migrate.
|
||||
În prezent este acceptat doar OAuth 2.0. Tipurile viitoare de acreditări (jetoane de acces personale, chei API, autentificare de bază) se vor integra în aceeași interfață — aplicațiile care deja folosesc `defineConnectionProvider({ type: 'oauth', ... })` nu vor trebui să migreze.
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="defineConnectionProvider" description="Declare how your app's connections are obtained">
|
||||
<Accordion title="defineConnectionProvider" description="Declară cum sunt obținute conexiunile aplicației tale">
|
||||
|
||||
A connection provider describes the OAuth handshake your app needs. The user clicks "Add connection" in your app's settings, completes the provider's consent screen, and a `ConnectedAccount` row is created in their workspace.
|
||||
Un furnizor de conexiune descrie handshake-ul OAuth de care are nevoie aplicația ta. Utilizatorul face clic pe "Adaugă conexiune" în setările aplicației tale, completează ecranul de consimțământ al furnizorului și este creată o înregistrare `ConnectedAccount` în spațiul său de lucru.
|
||||
|
||||
A working setup needs **two files** — the connection provider, and a matching `serverVariables` declaration on `defineApplication` that holds the OAuth client credentials.
|
||||
O configurație funcțională are nevoie de **două fișiere** — furnizorul de conexiune și o declarație `serverVariables` corespunzătoare în `defineApplication` care conține acreditările clientului OAuth.
|
||||
|
||||
```ts src/connection-providers/linear-connection.ts
|
||||
import { defineConnectionProvider } from 'twenty-sdk/define';
|
||||
@@ -71,16 +71,16 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Puncte cheie:
|
||||
|
||||
* `name` is the unique identifier string used in `listConnections({ providerName })` (kebab-case, must match `^[a-z][a-z0-9-]*$`).
|
||||
* `displayName` shows in the per-app settings tab and in the AI tool list.
|
||||
* `clientIdVariable` / `clientSecretVariable` are **names**, not values — they must match keys declared in `defineApplication.serverVariables`. The actual `client_id` and `client_secret` are entered by the server admin through the app registration UI, never committed to your repo.
|
||||
* Use `serverVariables` (not `applicationVariables`) — OAuth credentials are server-wide and one OAuth app per Twenty server.
|
||||
* Until both `serverVariables` are filled in, the per-app settings tab shows a "needs server admin" hint and the "Add connection" button is disabled.
|
||||
* `type: 'oauth'` is the only supported value today. The discriminator is forward-compatible: future types (`'pat'`, `'api-key'`, ...) will add new sub-config blocks alongside `oauth`.
|
||||
* `name` este șirul identificator unic folosit în `listConnections({ providerName })` (kebab-case, trebuie să corespundă `^[a-z][a-z0-9-]*$`).
|
||||
* `displayName` apare în fila de setări a aplicației și în lista de instrumente AI.
|
||||
* `clientIdVariable` / `clientSecretVariable` sunt **nume**, nu valori — trebuie să se potrivească cheilor declarate în `defineApplication.serverVariables`. Valorile reale `client_id` și `client_secret` sunt introduse de administratorul serverului prin interfața de înregistrare a aplicației și nu sunt niciodată comise în repo-ul tău.
|
||||
* Folosește `serverVariables` (nu `applicationVariables`) — acreditările OAuth sunt la nivel de server și există o singură aplicație OAuth pentru fiecare server Twenty.
|
||||
* Până când ambele `serverVariables` sunt completate, fila de setări a aplicației afișează un indiciu "necesită administrator de server" și butonul "Adaugă conexiune" este dezactivat.
|
||||
* `type: 'oauth'` este singura valoare acceptată în prezent. Discriminatorul este compatibil cu versiuni viitoare: tipurile viitoare (`'pat'`, `'api-key'`, ...) vor adăuga blocuri noi de sub-configurație alături de `oauth`.
|
||||
|
||||
The OAuth callback URL your provider needs to whitelist is:
|
||||
URL-ul de callback OAuth pe care furnizorul tău trebuie să îl includă pe lista albă este:
|
||||
|
||||
```
|
||||
https://<your-twenty-server>/apps/oauth/callback
|
||||
@@ -88,9 +88,9 @@ https://<your-twenty-server>/apps/oauth/callback
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="listConnections / getConnection" description="Use connections from a logic function">
|
||||
<Accordion title="listConnections / getConnection" description="Folosește conexiunile dintr-o funcție logică">
|
||||
|
||||
Inside a logic function handler, `listConnections({ providerName })` returns this app's `ConnectedAccount` rows for the given provider, with refreshed access tokens.
|
||||
În interiorul unui handler de funcție logică, `listConnections({ providerName })` returnează înregistrările `ConnectedAccount` ale acestei aplicații pentru furnizorul dat, cu tokenuri de acces reîmprospătate.
|
||||
|
||||
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
||||
import { listConnections } from 'twenty-sdk/logic-function';
|
||||
@@ -135,34 +135,34 @@ export const createLinearIssueHandler = async (input: {
|
||||
};
|
||||
```
|
||||
|
||||
Each connection has:
|
||||
Fiecare conexiune are:
|
||||
|
||||
| Field | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Unique row id; pass to `getConnection(id)` to refetch a single one |
|
||||
| `visibility` | `'user'` (private to one workspace member) or `'workspace'` (shared with all members) |
|
||||
| `scopes` | OAuth permissions granted by the upstream provider (distinct from `visibility` — those are unrelated) |
|
||||
| `userWorkspaceId` | The owner's userWorkspace id — useful for picking "the request user's connection" in HTTP-route triggers |
|
||||
| `accessToken` | Fresh OAuth access token (refreshed automatically if expired) |
|
||||
| `name` / `handle` | The connection's display name (auto-derived at OAuth callback, user-renameable) |
|
||||
| `authFailedAt` | Set when the most recent refresh failed; the user must reconnect |
|
||||
| Câmp | Descriere |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | ID unic al înregistrării; pasează-l la `getConnection(id)` pentru a reobține acea înregistrare |
|
||||
| `visibility` | `'user'` (privată pentru un membru al spațiului de lucru) sau `'workspace'` (partajată cu toți membrii) |
|
||||
| `scopes` | Permisiunile OAuth acordate de furnizorul upstream (distincte de `visibility` — nu au legătură) |
|
||||
| `userWorkspaceId` | ID-ul userWorkspace al deținătorului — util pentru a alege "conexiunea utilizatorului care face cererea" în declanșatoarele de rută HTTP |
|
||||
| `accessToken` | Token de acces OAuth proaspăt (reîmprospătat automat dacă a expirat) |
|
||||
| `name` / `handle` | Numele afișat al conexiunii (derivat automat la callback-ul OAuth, poate fi redenumit de utilizator) |
|
||||
| `authFailedAt` | Setat când cea mai recentă reîmprospătare a eșuat; utilizatorul trebuie să se reconecteze |
|
||||
|
||||
Key points:
|
||||
Puncte cheie:
|
||||
|
||||
* Pass `{ providerName }` to filter by provider; omit it to get all connections this app owns across all providers.
|
||||
* The server transparently refreshes the access token before returning. Your handler always sees a usable token (or `authFailedAt` set).
|
||||
* `getConnection(id)` is the single-row equivalent.
|
||||
* Pasează `{ providerName }` pentru a filtra după furnizor; omite-l pentru a obține toate conexiunile pe care această aplicație le deține la toți furnizorii.
|
||||
* Serverul reîmprospătează transparent tokenul de acces înainte de a returna. Handlerul tău vede întotdeauna un token utilizabil (sau `authFailedAt` setat).
|
||||
* `getConnection(id)` este echivalentul pentru o singură înregistrare.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Per-user vs workspace-shared visibility" description="How users choose between private and shared credentials">
|
||||
<Accordion title="Vizibilitate per utilizator vs partajată la nivel de spațiu de lucru" description="Cum aleg utilizatorii între acreditări private și partajate">
|
||||
|
||||
When a user clicks "Add connection," they're prompted to pick a visibility:
|
||||
Când un utilizator face clic pe "Adaugă conexiune", i se solicită să aleagă o vizibilitate:
|
||||
|
||||
* **Just for me** — the credential is private to the connecting user. Any logic function called on their behalf (HTTP-route trigger with `isAuthRequired: true`) sees it; cron triggers and database events do not.
|
||||
* **Workspace shared** — any workspace member can use the credential. Cron / database triggers also see it, since they have no request user.
|
||||
* **Doar pentru mine** — acreditarea este privată pentru utilizatorul care se conectează. Orice funcție logică apelată în numele lor (declanșator de rută HTTP cu `isAuthRequired: true`) o vede; declanșatoarele cron și evenimentele din bază de date nu.
|
||||
* **Partajată la nivel de spațiu de lucru** — orice membru al spațiului de lucru poate folosi acreditarea. Declanșatoarele cron / din bază de date o văd, de asemenea, deoarece nu au un utilizator al cererii.
|
||||
|
||||
Use the right one for each handler:
|
||||
Folosește-o pe cea potrivită pentru fiecare handler:
|
||||
|
||||
```ts
|
||||
// HTTP-route trigger — prefer the request user's own connection.
|
||||
@@ -174,19 +174,19 @@ const conn =
|
||||
const conn = connections.find((c) => c.visibility === 'workspace');
|
||||
```
|
||||
|
||||
Multiple connections per (user, provider) are allowed, so the same user can hold "Personal Linear" and "Work Linear" side by side.
|
||||
Sunt permise mai multe conexiuni per (utilizator, furnizor), astfel încât același utilizator poate avea "Personal Linear" și "Work Linear" una lângă alta.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="One-time provider setup" description="Register your OAuth app with the third-party service">
|
||||
<Accordion title="Configurare unică a furnizorului" description="Înregistrează-ți aplicația OAuth la serviciul terț">
|
||||
|
||||
For each connection provider, the server admin needs to register an OAuth app at the third party first.
|
||||
Pentru fiecare furnizor de conexiune, administratorul serverului trebuie mai întâi să înregistreze o aplicație OAuth la serviciul terț.
|
||||
|
||||
1. Go to the provider's developer settings (e.g. https://linear.app/settings/api/applications/new).
|
||||
2. Set the **Redirect URI** to `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Copy the generated **Client ID** and **Client Secret**.
|
||||
4. Open the installed app in Twenty as a server admin → set the values on the corresponding `serverVariables`.
|
||||
5. Workspace members can then add connections from the per-app **Connections** section.
|
||||
1. Mergi la setările pentru dezvoltatori ale furnizorului (de ex. https://linear.app/settings/api/applications/new).
|
||||
2. Setează **Redirect URI** la `\<SERVER_URL>/apps/oauth/callback`.
|
||||
3. Copiază **Client ID** și **Client Secret** generate.
|
||||
4. Deschide aplicația instalată în Twenty ca administrator de server → setează valorile pe `serverVariables` corespunzătoare.
|
||||
5. Membrii spațiului de lucru pot apoi să adauge conexiuni din secțiunea **Conexiuni** a fiecărei aplicații.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ export default defineLogicFunction({
|
||||
</AccordionGroup>
|
||||
|
||||
<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](/l/ro/developers/extend/apps/config/install-hooks) for `definePreInstallLogicFunction` and `definePostInstallLogicFunction`.
|
||||
**Hook-uri de instalare** — handleri pre-instalare și post-instalare — partajează acest runtime, dar sunt declarați cu propriile lor funcții `define` și nu folosesc setări de declanșare. Consultați [Hook-uri de instalare](/l/ro/developers/extend/apps/config/install-hooks) pentru `definePreInstallLogicFunction` și `definePostInstallLogicFunction`.
|
||||
</Note>
|
||||
|
||||
## Clienți API tipizați (twenty-client-sdk)
|
||||
@@ -269,7 +269,7 @@ const { createCompany } = await client.mutation({
|
||||
});
|
||||
```
|
||||
|
||||
Clientul folosește o sintaxă de tip selection-set: transmiteți `true` pentru a include un câmp, folosiți `__args` pentru argumente și imbricați obiecte pentru relații. Obțineți autocompletare și verificare a tipurilor complete, pe baza schemei spațiului dvs. de lucru.
|
||||
Clientul folosește o sintaxă de tip selection-set: transmiteți `true` pentru a include un câmp, folosiți `__args` pentru argumente și imbricați obiecte pentru relații. Obțineți autocompletare și verificare completă a tipurilor, pe baza schemei spațiului dvs. de lucru.
|
||||
|
||||
<Note>
|
||||
**CoreApiClient este generat în timpul dev/build.** Dacă îl utilizați fără a rula mai întâi `yarn twenty dev` sau `yarn twenty build`, va arunca o eroare. Generarea are loc automat — CLI inspectează schema GraphQL a spațiului dvs. de lucru și generează un client tipizat folosind `@genql/cli`.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Prezentare generală
|
||||
description: Server-side TypeScript that runs inside Twenty — triggered by HTTP routes, cron schedules, database events, AI tools, or workflow actions.
|
||||
description: TypeScript pe server care rulează în Twenty — declanșat de rute HTTP, programări cron, evenimente de bază de date, instrumente IA sau acțiuni de workflow.
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
A Twenty app's **logic layer** is the code that *runs* — server-side TypeScript handlers reacting to HTTP requests, cron schedules, and record changes; AI skills and agents that live inside the workspace; and OAuth connections that let your functions act on a user's behalf in third-party services.
|
||||
**Stratul de logică** al unei aplicații Twenty este codul care *rulează* — handler-e TypeScript pe server care reacționează la cereri HTTP, programări cron și modificări ale înregistrărilor; abilități și agenți IA care trăiesc în workspace; și conexiuni OAuth care permit funcțiilor tale să acționeze în numele utilizatorului în servicii terțe.
|
||||
|
||||
```text
|
||||
┌─ HTTP route ──┐
|
||||
@@ -22,34 +22,34 @@ A Twenty app's **logic layer** is the code that *runs* — server-side TypeScrip
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## În această secțiune
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Funcții logice" icon="bolt" href="/l/ro/developers/extend/apps/logic/logic-functions">
|
||||
The core building block — trigger types, payloads, and the typed API client.
|
||||
Blocul de bază — tipuri de declanșatoare, payload-uri și clientul API cu tipuri.
|
||||
</Card>
|
||||
<Card title="Abilități și agenți" icon="robot" href="/l/ro/developers/extend/apps/logic/skills-and-agents">
|
||||
Reusable AI agent instructions and assistants with custom system prompts.
|
||||
Instrucțiuni reutilizabile pentru agenți IA și asistenți cu prompturi de sistem personalizate.
|
||||
</Card>
|
||||
<Card title="Conexiuni" icon="plug" href="/l/ro/developers/extend/apps/logic/connections">
|
||||
OAuth credentials your app holds for third-party services — Linear, GitHub, Slack, and more.
|
||||
Credențiale OAuth pe care aplicația ta le deține pentru servicii terțe — Linear, GitHub, Slack și altele.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Trigger types at a glance
|
||||
## Tipuri de declanșatoare, dintr-o privire
|
||||
|
||||
A logic function picks one or more triggers — every entry below is a separate field on `defineLogicFunction()`:
|
||||
O funcție de logică alege unul sau mai multe declanșatoare — fiecare intrare de mai jos este un câmp separat pe `defineLogicFunction()`:
|
||||
|
||||
| Declanșator | When it runs | Setare |
|
||||
| -------------------------- | ---------------------------------------------------------- | ------------------------------- |
|
||||
| **HTTP route** | A request hits your `/s/\<path>` endpoint | `httpRouteTriggerSettings` |
|
||||
| **Cron** | A CRON expression matches | `cronTriggerSettings` |
|
||||
| **Eveniment baza de date** | A workspace record is created, updated, or deleted | `databaseEventTriggerSettings` |
|
||||
| **AI tool** | A Twenty AI feature decides to call your function | `toolTriggerSettings` |
|
||||
| **Acțiune Workflow** | A workflow step invokes your function | `workflowActionTriggerSettings` |
|
||||
| Declanșator | Când rulează | Setare |
|
||||
| -------------------------- | ----------------------------------------------------------------- | ------------------------------- |
|
||||
| **Rută HTTP** | O cerere ajunge la endpointul tău `/s/\<path>` | `httpRouteTriggerSettings` |
|
||||
| **Cron** | Se potrivește o expresie CRON | `cronTriggerSettings` |
|
||||
| **Eveniment baza de date** | O înregistrare din workspace este creată, actualizată sau ștearsă | `databaseEventTriggerSettings` |
|
||||
| **Instrument IA** | O funcționalitate Twenty IA decide să apeleze funcția ta | `toolTriggerSettings` |
|
||||
| **Acțiune Workflow** | Un pas din workflow îți invocă funcția | `workflowActionTriggerSettings` |
|
||||
|
||||
Functions run sandboxed in isolated Node.js processes and access the workspace through a typed API client scoped to the role declared on [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
|
||||
Funcțiile rulează în sandbox, în procese Node.js izolate și accesează workspace-ul printr-un client API cu tipuri, limitat la rolul declarat în [`defineApplication()`](/l/ro/developers/extend/apps/config/application).
|
||||
|
||||
<Note>
|
||||
**Install-time hooks** — code that runs before or after the install — share this runtime but use their own define functions and live under [Config → Install Hooks](/l/ro/developers/extend/apps/config/install-hooks).
|
||||
**Hook-uri de instalare** — cod care rulează înainte sau după instalare — folosesc același runtime, dar au propriile lor funcții define și se află sub [Config → Install Hooks](/l/ro/developers/extend/apps/config/install-hooks).
|
||||
</Note>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
title: Skills & Agents
|
||||
description: Define AI skills and agents for your app.
|
||||
title: Abilități și agenți
|
||||
description: Definiți abilități și agenți AI pentru aplicația dvs.
|
||||
icon: robot
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Skills and agents are currently in alpha. The feature works but is still evolving.
|
||||
Aptitudinile și agenții sunt în prezent în testare alfa. Caracteristica funcționează, dar este încă în dezvoltare.
|
||||
</Warning>
|
||||
|
||||
Apps can define AI capabilities that live inside the workspace — reusable skill instructions and agents with custom system prompts.
|
||||
Aplicațiile pot defini capabilități AI care există în interiorul spațiului de lucru — instrucțiuni reutilizabile pentru abilități și agenți cu prompturi de sistem personalizate.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineSkill" description="Define AI agent skills">
|
||||
<Accordion title="defineSkill" description="Definiți abilități pentru agentul AI">
|
||||
|
||||
Skills define reusable instructions and capabilities that AI agents can use within your workspace. Use `defineSkill()` to define skills with built-in validation:
|
||||
Abilitățile definesc instrucțiuni și capabilități reutilizabile pe care agenții AI le pot folosi în spațiul dvs. de lucru. Folosiți `defineSkill()` pentru a defini abilități cu validare încorporată:
|
||||
|
||||
```ts src/skills/example-skill.ts
|
||||
import { defineSkill } from 'twenty-sdk/define';
|
||||
@@ -32,17 +32,17 @@ export default defineSkill({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is a unique identifier string for the skill (kebab-case recommended).
|
||||
* `label` is the human-readable display name shown in the UI.
|
||||
* `content` contains the skill instructions — this is the text the AI agent uses.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `description` (optional) provides additional context about the skill's purpose.
|
||||
Puncte cheie:
|
||||
* `name` este un șir identificator unic pentru abilitate (se recomandă kebab-case).
|
||||
* `label` este numele lizibil afișat în interfața cu utilizatorul (UI).
|
||||
* `content` conține instrucțiunile abilității — acesta este textul pe care agentul AI îl folosește.
|
||||
* `icon` (opțional) setează pictograma afișată în UI.
|
||||
* `description` (opțional) oferă context suplimentar despre scopul abilității.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="defineAgent" description="Define AI agents with custom prompts">
|
||||
<Accordion title="defineAgent" description="Definiți agenți AI cu prompturi personalizate">
|
||||
|
||||
Agents are AI assistants that live inside your workspace. Use `defineAgent()` to create agents with a custom system prompt:
|
||||
Agenții sunt asistenți AI care există în interiorul spațiului dvs. de lucru. Utilizați `defineAgent()` pentru a crea agenți cu un prompt de sistem personalizat:
|
||||
|
||||
```ts src/agents/example-agent.ts
|
||||
import { defineAgent } from 'twenty-sdk/define';
|
||||
@@ -57,13 +57,13 @@ export default defineAgent({
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
* `name` is the unique identifier string for the agent (kebab-case recommended).
|
||||
* `label` is the display name shown in the UI.
|
||||
* `prompt` is the system prompt that defines the agent's behavior.
|
||||
* `description` (optional) provides context about what the agent does.
|
||||
* `icon` (optional) sets the icon displayed in the UI.
|
||||
* `modelId` (optional) overrides the default AI model used by the agent.
|
||||
Puncte cheie:
|
||||
* `name` este un șir identificator unic pentru agent (se recomandă kebab-case).
|
||||
* `label` este numele de afișare din interfața cu utilizatorul (UI).
|
||||
* `prompt` conține promptul de sistem — acesta este textul de instrucțiuni care definește comportamentul agentului.
|
||||
* `description` (opțional) oferă context suplimentar despre scopul agentului.
|
||||
* `icon` (opțional) setează pictograma afișată în UI.
|
||||
* `modelId` (opțional) suprascrie modelul AI implicit utilizat de agent.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: CLI
|
||||
description: yarn twenty commands for executing functions, streaming logs, managing app installations, and switching remotes.
|
||||
description: comenzi `yarn twenty` pentru executarea funcțiilor, transmiterea fluxurilor de jurnale, gestionarea instalărilor de aplicații și schimbarea remote-urilor.
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
Beyond `dev`, `build`, `add`, and `typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
|
||||
Dincolo de `dev`, `build`, `add` și `typecheck`, `yarn twenty` CLI oferă comenzi pentru executarea funcțiilor, vizualizarea jurnalelor și gestionarea instalărilor de aplicații.
|
||||
|
||||
## Executing functions (`yarn twenty exec`)
|
||||
## Executarea funcțiilor (`yarn twenty exec`)
|
||||
|
||||
Run a logic function manually without triggering it via HTTP, cron, or database event:
|
||||
Rulați manual o funcție logică fără a o declanșa prin HTTP, cron sau eveniment de bază de date:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Execute by function name
|
||||
@@ -24,9 +24,9 @@ yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
## Viewing function logs (`yarn twenty logs`)
|
||||
## Vizualizarea jurnalelor funcțiilor (`yarn twenty logs`)
|
||||
|
||||
Stream execution logs for your app's logic functions:
|
||||
Transmiteți în flux jurnalele de execuție pentru funcțiile logice ale aplicației:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Stream all function logs
|
||||
@@ -40,12 +40,12 @@ yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is different from `yarn twenty server logs`, which shows the Docker container logs. `yarn twenty logs` shows your app's function execution logs from the Twenty server.
|
||||
Acest lucru este diferit de `yarn twenty server logs`, care afișează jurnalele containerului Docker. `yarn twenty logs` afișează jurnalele de execuție ale funcțiilor aplicației de pe serverul Twenty.
|
||||
</Note>
|
||||
|
||||
## Uninstalling an app (`yarn twenty uninstall`)
|
||||
## Dezinstalarea unei aplicații (`yarn twenty uninstall`)
|
||||
|
||||
Remove your app from the active workspace:
|
||||
Eliminați aplicația din spațiul de lucru activ:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty uninstall
|
||||
@@ -54,9 +54,9 @@ yarn twenty uninstall
|
||||
yarn twenty uninstall --yes
|
||||
```
|
||||
|
||||
## Managing remotes
|
||||
## Gestionarea remote-urilor
|
||||
|
||||
A **remote** is a Twenty server that your app connects to. During setup, the scaffolder creates one for you automatically. You can add more remotes or switch between them at any time.
|
||||
Un „remote” este un server Twenty la care se conectează aplicația. În timpul configurării, Scaffolderul creează automat unul pentru dvs. Puteți adăuga mai multe remote-uri sau comuta între ele oricând.
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new remote (opens a browser for OAuth login)
|
||||
@@ -75,4 +75,4 @@ yarn twenty remote list
|
||||
yarn twenty remote switch <name>
|
||||
```
|
||||
|
||||
Your credentials are stored in `~/.twenty/config.json`.
|
||||
Acreditările dvs. sunt stocate în `~/.twenty/config.json`.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Prezentare generală
|
||||
description: Build, test, and ship your app — CLI commands, integration tests, CI, and publishing to a server or to npm.
|
||||
description: Construiește, testează și livrează aplicația ta — comenzi CLI, teste de integrare, CI și publicare pe un server sau pe npm.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
The **operations layer** is everything you do *to* your app rather than *with* it: invoking CLI commands, running integration tests against a real Twenty server, configuring CI, and shipping releases — either as a tarball deployed to a single server or as an npm package listed in the marketplace.
|
||||
**Stratul de operațiuni** este tot ceea ce faci *asupra* aplicației tale, mai degrabă decât *cu* ea: rularea de comenzi CLI, executarea de teste de integrare împotriva unui server Twenty real, configurarea CI și livrarea de versiuni — fie ca un tarball distribuit pe un singur server, fie ca un pachet npm listat în marketplace.
|
||||
|
||||
```text
|
||||
develop ─▶ test ─▶ build ─▶ deploy / publish
|
||||
@@ -14,16 +14,16 @@ The **operations layer** is everything you do *to* your app rather than *with* i
|
||||
dev build yarn twenty publish (npm → marketplace)
|
||||
```
|
||||
|
||||
## In this section
|
||||
## În această secțiune
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="CLI" icon="terminal" href="/l/ro/developers/extend/apps/operations/cli">
|
||||
`yarn twenty` reference — exec, logs, uninstall, remotes.
|
||||
</Card>
|
||||
<Card title="Testare" icon="flask" href="/l/ro/developers/extend/apps/operations/testing">
|
||||
Vitest setup, integration tests, type checking, CI workflow.
|
||||
Configurare Vitest, teste de integrare, verificare de tipuri, workflow CI.
|
||||
</Card>
|
||||
<Card title="Publicare" icon="încarcă" href="/l/ro/developers/extend/apps/operations/publishing">
|
||||
Build, deploy a tarball, publish to npm, install.
|
||||
Construire, deploy al unui tarball, publicare pe npm, instalare.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Testare
|
||||
description: Vitest setup, integration tests against a real Twenty server, type checking, and CI with GitHub Actions.
|
||||
description: Configurare Vitest, teste de integrare pe un server Twenty real, verificare a tipurilor și CI cu GitHub Actions.
|
||||
icon: flask
|
||||
---
|
||||
|
||||
SDK-ul oferă API-uri programatice care vă permit să construiți, să distribuiți, să instalați și să dezinstalați aplicația din codul de test. Combinat cu [Vitest](https://vitest.dev/) și clienții API tipizați, puteți scrie teste de integrare care verifică faptul că aplicația funcționează cap-coadă împotriva unui server Twenty real.
|
||||
SDK-ul oferă API-uri programatice care vă permit să construiți, să distribuiți, să instalați și să dezinstalați aplicația din codul de test. Combinat cu [Vitest](https://vitest.dev/) și clienții API tipizați, puteți scrie teste de integrare care verifică faptul că aplicația funcționează de la un cap la altul pe un server Twenty real.
|
||||
|
||||
## Utilizarea pachetelor npm
|
||||
|
||||
@@ -140,7 +140,7 @@ beforeAll(async () => {
|
||||
|
||||
## API-uri SDK programatice
|
||||
|
||||
Subruta `twenty-sdk/cli` exportă funcții pe care le puteți apela direct din codul de test:
|
||||
Subcalea `twenty-sdk/cli` exportă funcții pe care le puteți apela direct din codul de test:
|
||||
|
||||
| Funcție | Descriere |
|
||||
| -------------- | --------------------------------------------------------- |
|
||||
|
||||
@@ -155,7 +155,27 @@
|
||||
"label": "Prezentare generală"
|
||||
},
|
||||
"apps": {
|
||||
"label": "Aplicații"
|
||||
"label": "Aplicații",
|
||||
"groups": {
|
||||
"appsGettingStarted": {
|
||||
"label": "Începeți"
|
||||
},
|
||||
"appsConfig": {
|
||||
"label": "Configurare"
|
||||
},
|
||||
"appsData": {
|
||||
"label": "Date"
|
||||
},
|
||||
"appsLogic": {
|
||||
"label": "Logică"
|
||||
},
|
||||
"appsLayout": {
|
||||
"label": "Aspect"
|
||||
},
|
||||
"appsOperations": {
|
||||
"label": "Operațiuni"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"label": "API"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Application Config
|
||||
description: Declare your app's identity, default role, variables, and marketplace metadata with defineApplication.
|
||||
title: Конфигурация приложения
|
||||
description: Объявите идентификацию вашего приложения, роль по умолчанию, переменные и метаданные маркетплейса с помощью defineApplication.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
Every app must have exactly one `defineApplication` call. It declares:
|
||||
В каждом приложении должен быть ровно один вызов `defineApplication`. Он объявляет:
|
||||
|
||||
* **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](/l/ru/developers/extend/apps/logic/logic-functions).
|
||||
* **Идентификация** — универсальный идентификатор, отображаемое имя, описание.
|
||||
* **Разрешения** — под какой ролью выполняются его логические функции и фронтенд-компоненты.
|
||||
* **Переменные** *(необязательно)* — пары ключ–значение, доступные вашему коду как переменные окружения.
|
||||
* **Хуки предустановки / постустановки** *(необязательно)* — см. [Логические функции](/l/ru/developers/extend/apps/logic/logic-functions).
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
@@ -31,35 +31,35 @@ export default defineApplication({
|
||||
});
|
||||
```
|
||||
|
||||
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 (e.g., `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` must reference a role defined with [`defineRole()`](/l/ru/developers/extend/apps/config/roles).
|
||||
* Pre-install and post-install functions are detected automatically during the manifest build — you do not need to reference them in `defineApplication()`.
|
||||
* Поля `universalIdentifier` — это детерминированные идентификаторы, которые принадлежат вам. Сгенерируйте их один раз и сохраняйте неизменными между синхронизациями.
|
||||
* `applicationVariables` становятся переменными окружения для ваших функций и фронтенд-компонентов (например, `DEFAULT_RECIPIENT_NAME` доступна как `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` должен ссылаться на роль, определённую с помощью [`defineRole()`](/l/ru/developers/extend/apps/config/roles).
|
||||
* Предустановочные и постустановочные функции обнаруживаются автоматически во время сборки манифеста — вам не нужно указывать их в `defineApplication()`.
|
||||
|
||||
## Default function role
|
||||
## Роль функции по умолчанию
|
||||
|
||||
The `defaultRoleUniversalIdentifier` controls what the app's logic functions and front components can access:
|
||||
`defaultRoleUniversalIdentifier` определяет, к чему могут получать доступ логические функции и фронтенд-компоненты приложения:
|
||||
|
||||
* The runtime token injected as `TWENTY_APP_ACCESS_TOKEN` is derived from this role.
|
||||
* The typed API client is restricted to the permissions granted to that role.
|
||||
* Follow least-privilege: declare only the permissions your functions need.
|
||||
* Токен времени выполнения, подставляемый как `TWENTY_APP_ACCESS_TOKEN`, формируется из этой роли.
|
||||
* Типизированный клиент API ограничен правами, предоставленными этой роли.
|
||||
* Следуйте принципу наименьших привилегий: объявляйте только те разрешения, которые действительно нужны вашим функциям.
|
||||
|
||||
When you scaffold a new app, the CLI creates a starter role file at `src/roles/default-role.ts`. See [Roles & Permissions](/l/ru/developers/extend/apps/config/roles) for the full reference.
|
||||
Когда вы создаёте новое приложение с помощью шаблона, CLI создаёт стартовый файл роли по адресу `src/roles/default-role.ts`. Полную справочную информацию см. в разделе [Роли и разрешения](/l/ru/developers/extend/apps/config/roles).
|
||||
|
||||
## Marketplace metadata
|
||||
## Метаданные маркетплейса
|
||||
|
||||
If you plan to [publish your app](/l/ru/developers/extend/apps/operations/publishing), these optional fields control how it appears in the marketplace:
|
||||
Если вы планируете [опубликовать приложение](/l/ru/developers/extend/apps/operations/publishing), эти необязательные поля определяют, как оно отображается в маркетплейсе:
|
||||
|
||||
| Field | Description |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Author or company name |
|
||||
| `category` | App category for marketplace filtering |
|
||||
| `logoUrl` | Path to your app logo (e.g., `public/logo.png`) |
|
||||
| `screenshots` | Array of screenshot paths (e.g., `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm |
|
||||
| `websiteUrl` | Link to your website |
|
||||
| `termsUrl` | Link to terms of service |
|
||||
| `emailSupport` | Support email address |
|
||||
| `issueReportUrl` | Link to issue tracker |
|
||||
| Поле | Описание |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `author` | Имя автора или название компании |
|
||||
| `category` | Категория приложения для фильтрации в маркетплейсе |
|
||||
| `logoUrl` | Путь к логотипу вашего приложения (например, `public/logo.png`) |
|
||||
| `screenshots` | Массив путей к скриншотам (например, `public/screenshot-1.png`) |
|
||||
| `aboutDescription` | Расширенное описание в Markdown для вкладки "About". Если опущено, маркетплейс использует `README.md` пакета из npm |
|
||||
| `websiteUrl` | Ссылка на ваш сайт |
|
||||
| `termsUrl` | Ссылка на условия предоставления услуг |
|
||||
| `emailSupport` | Адрес электронной почты поддержки |
|
||||
| `issueReportUrl` | Ссылка на систему отслеживания проблем |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Install Hooks
|
||||
description: Run logic before or after the install — seed data, back up records, validate the upgrade.
|
||||
title: Установочные хуки
|
||||
description: Запускайте логику до или после установки — заполняйте исходные данные, создавайте резервные копии записей, проверяйте корректность обновления.
|
||||
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](/l/ru/developers/extend/apps/logic/logic-functions) and receive an `InstallPayload`, but they're declared with their own define functions — `definePostInstallLogicFunction()` and `definePreInstallLogicFunction()` — and live outside the normal trigger model (HTTP, cron, database events).
|
||||
Установочные хуки — это специальные логические функции, которые выполняются во время установки или обновления. Они используют то же окружение выполнения обработчика, что и обычные [logic functions](/l/ru/developers/extend/apps/logic/logic-functions) и получают `InstallPayload`, но объявляются с помощью собственных функций определения — `definePostInstallLogicFunction()` и `definePreInstallLogicFunction()` — и существуют вне обычной модели триггеров (HTTP, cron, события базы данных).
|
||||
|
||||
Each app may define **at most one pre-install** and **at most one post-install** function. The manifest build will error if more than one of either is detected.
|
||||
Каждое приложение может определить **не более одной pre-install** и **не более одной post-install** функции. Сборка манифеста завершится ошибкой, если будет обнаружено более одной функции любого из этих типов.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
@@ -20,9 +20,9 @@ Each app may define **at most one pre-install** and **at most one post-install**
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="Runs after the workspace metadata migration is applied">
|
||||
<Accordion title="definePostInstallLogicFunction" description="Выполняется после применения миграции метаданных рабочего пространства">
|
||||
|
||||
A post-install function runs automatically once your app has finished installing on a workspace. The server executes it **after** the app's metadata has been synchronized and the SDK client has been generated, so the workspace is fully ready to use and the new schema is in place. Typical use cases include seeding default data, creating initial records, configuring workspace settings, or provisioning resources on third-party services.
|
||||
Послеустановочная функция автоматически запускается после того, как установка вашего приложения в рабочем пространстве завершена. Сервер выполняет её **после** того, как метаданные приложения синхронизированы и клиент SDK сгенерирован, так что рабочее пространство полностью готово к использованию, а новая схема уже применена. Типичные сценарии использования включают предзаполнение данных по умолчанию, создание начальных записей, настройку параметров рабочего пространства или выделение ресурсов в сторонних сервисах.
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -42,30 +42,30 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the post-install function at any time using the CLI:
|
||||
Вы также можете вручную выполнить постустановочную функцию в любое время с помощью CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --postInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Post-install functions use `definePostInstallLogicFunction()` — a specialized variant that omits trigger settings (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* The handler receives an `InstallPayload` with `{ previousVersion?: string; newVersion: string }` — `newVersion` is the version being installed, and `previousVersion` is the version that was previously installed (or `undefined` on a fresh install). Use these values to distinguish fresh installs from upgrades and to run version-specific migration logic.
|
||||
* **When the hook runs**: on fresh installs only, by default. Pass `shouldRunOnVersionUpgrade: true` if you also want it to run when the app is upgraded from a previous version. When omitted, the flag defaults to `false` and upgrades skip the hook.
|
||||
* **Execution model — async by default, sync opt-in**: the `shouldRunSynchronously` flag controls *how* post-install is executed.
|
||||
* `shouldRunSynchronously: false` *(default)* — the hook is **enqueued on the message queue** with `retryLimit: 3` and runs asynchronously in a worker. The install response returns as soon as the job is enqueued, so a slow or failing handler does not block the caller. The worker will retry up to three times. **Use this for long-running jobs** — seeding large datasets, calling slow third-party APIs, provisioning external resources, anything that might exceed a reasonable HTTP response window.
|
||||
* `shouldRunSynchronously: true` — the hook is executed **inline during the install flow** (same executor as pre-install). The install request blocks until the handler finishes, and if it throws, the install caller receives a `POST_INSTALL_ERROR`. No automatic retries. **Use this for fast, must-complete-before-response work** — for example, emitting a validation error to the user, or quick setup that the client will rely on immediately after the install call returns. Keep in mind the metadata migration has already been applied by the time post-install runs, so a sync-mode failure does **not** roll back the schema changes — it only surfaces the error.
|
||||
* Make sure your handler is idempotent. In async mode the queue may retry up to three times; in either mode the hook may run again on upgrades when `shouldRunOnVersionUpgrade: true`.
|
||||
* The environment variables `APPLICATION_ID`, `APP_ACCESS_TOKEN`, and `API_URL` are available inside the handler (same as any other logic function), so you can call the Twenty API with an application access token scoped to your app.
|
||||
* Only one post-install function is allowed per application. The manifest build will error if more than one is detected.
|
||||
* The function's `universalIdentifier`, `shouldRunOnVersionUpgrade`, and `shouldRunSynchronously` are automatically attached to the application manifest under the `postInstallLogicFunction` field during the build — you do not need to reference them in [`defineApplication()`](/l/ru/developers/extend/apps/config/application).
|
||||
* The default timeout is set to 300 seconds (5 minutes) to allow for longer setup tasks like data seeding.
|
||||
* **Not executed in dev mode**: when an app is registered locally (via `yarn twenty dev`), the server skips the install flow entirely and syncs files directly through the CLI watcher — so post-install never runs in dev mode, regardless of `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` to trigger it manually against a running workspace.
|
||||
Основные моменты:
|
||||
* Послеустановочные функции используют `definePostInstallLogicFunction()` — специализированный вариант, который опускает настройки триггеров (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`).
|
||||
* Обработчик получает `InstallPayload` с `{ previousVersion?: string; newVersion: string }` — `newVersion` — это устанавливаемая версия, а `previousVersion` — версия, установленная ранее (или `undefined` при чистой установке). Используйте эти значения, чтобы отличать чистые установки от обновлений и запускать логику миграции, зависящую от версии.
|
||||
* **Когда запускается хук**: по умолчанию только при чистой установке. Передайте `shouldRunOnVersionUpgrade: true`, если хотите, чтобы он также выполнялся при обновлении приложения с предыдущей версии. Если флаг опущен, по умолчанию он равен `false`, и при обновлении хук пропускается.
|
||||
* **Модель выполнения — по умолчанию асинхронно, синхронный режим по выбору**: флаг `shouldRunSynchronously` определяет, *как* выполняется post-install.
|
||||
* `shouldRunSynchronously: false` *(по умолчанию)* — хук **помещается в очередь сообщений** с `retryLimit: 3` и выполняется асинхронно в воркере. Ответ на установку возвращается сразу после постановки задания в очередь, поэтому медленный или дающий сбой обработчик не блокирует вызывающую сторону. Воркер выполнит до трёх повторных попыток. **Используйте это для длительных задач** — наполнение большими наборами данных, вызовы медленных сторонних API, подготовка внешних ресурсов — всего, что может выйти за разумное окно ответа HTTP.
|
||||
* `shouldRunSynchronously: true` — хук выполняется **непосредственно в процессе установки** (тот же исполнитель, что и для pre-install). Запрос установки блокируется, пока обработчик не завершится, и если он генерирует исключение, вызывающая сторона установки получает `POST_INSTALL_ERROR`. Автоматических повторов нет. **Используйте это для быстрых задач, которые должны завершиться до отправки ответа** — например, выдача ошибки валидации пользователю или быстрая настройка, на которую клиент будет полагаться сразу после возврата вызова установки. Имейте в виду, что к моменту запуска post-install миграция метаданных уже применена, поэтому сбой в синхронном режиме **не** откатывает изменения схемы — он лишь выявляет ошибку.
|
||||
* Убедитесь, что ваш обработчик идемпотентен. В асинхронном режиме очередь может выполнить до трёх повторных попыток; в любом режиме хук может запускаться снова при обновлениях, когда `shouldRunOnVersionUpgrade: true`.
|
||||
* Переменные окружения `APPLICATION_ID`, `APP_ACCESS_TOKEN` и `API_URL` доступны внутри обработчика (как и в любой другой логической функции), поэтому вы можете вызывать API Twenty с токеном доступа приложения, ограниченным вашим приложением.
|
||||
* Для каждого приложения допускается только одна послеустановочная функция. Сборка манифеста завершится ошибкой, если будет обнаружено более одной такой функции.
|
||||
* Параметры функции `universalIdentifier`, `shouldRunOnVersionUpgrade` и `shouldRunSynchronously` автоматически добавляются в манифест приложения в поле `postInstallLogicFunction` во время сборки — вам не нужно указывать их в [`defineApplication()`](/l/ru/developers/extend/apps/config/application).
|
||||
* Тайм-аут по умолчанию установлен на 300 секунд (5 минут), чтобы позволить выполнять более длительные задачи настройки, такие как инициализация данных.
|
||||
* **Не выполняется в режиме разработки**: когда приложение зарегистрировано локально (через `yarn twenty dev`), сервер полностью пропускает процесс установки и синхронизирует файлы напрямую через наблюдатель CLI — поэтому post-install никогда не запускается в режиме разработки, независимо от `shouldRunSynchronously`. Используйте `yarn twenty exec --postInstall`, чтобы запустить это вручную для запущенного рабочего пространства.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="Runs before the workspace metadata migration is applied">
|
||||
<Accordion title="definePreInstallLogicFunction" description="Выполняется до применения миграции метаданных рабочего пространства">
|
||||
|
||||
A pre-install function runs automatically during installation, **before the workspace metadata migration is applied**. It shares the same payload shape as post-install (`InstallPayload`), but it is positioned earlier in the install flow so it can prepare state that the upcoming migration depends on — typical uses include backing up data, validating compatibility with the new schema, or archiving records that are about to be restructured or dropped.
|
||||
Функция pre-install автоматически выполняется во время установки, **до применения миграции метаданных рабочего пространства**. Она использует ту же структуру полезной нагрузки, что и post-install (`InstallPayload`), но находится раньше в процессе установки, чтобы подготовить состояние, от которого зависит предстоящая миграция, — типичные сценарии включают резервное копирование данных, проверку совместимости с новой схемой или архивирование записей, которые будут реструктурированы или удалены.
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -84,35 +84,35 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the pre-install function at any time using the CLI:
|
||||
Вы также можете вручную выполнить предустановочную функцию в любое время с помощью CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty exec --preInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
* Pre-install functions use `definePreInstallLogicFunction()` — same specialized config as post-install, just attached to a different lifecycle slot.
|
||||
* Both pre- and post-install handlers receive the same `InstallPayload` type: `{ previousVersion?: string; newVersion: string }`. Import it once and reuse it for both hooks.
|
||||
* **When the hook runs**: positioned just before the workspace metadata migration (`synchronizeFromManifest`). Before executing, the server runs a purely additive "pared-down sync" that registers the **new** version's pre-install function in the workspace metadata — nothing else is touched — and then executes it. Because this sync is additive-only, the previous version's objects, fields, and data are still intact when your handler runs: you can safely read and back up pre-migration state.
|
||||
* **Execution model**: pre-install is executed **synchronously** and **blocks the install**. If the handler throws, the install is aborted before any schema changes are applied — the workspace stays on the previous version in a consistent state. This is intentional: pre-install is your last chance to refuse a risky upgrade.
|
||||
* As with post-install, only one pre-install function is allowed per application. It is attached to the application manifest under `preInstallLogicFunction` automatically during the build.
|
||||
* **Not executed in dev mode**: same as post-install — the install flow is skipped entirely for locally-registered apps, so pre-install never runs under `yarn twenty dev`. Use `yarn twenty exec --preInstall` to trigger it manually.
|
||||
Основные моменты:
|
||||
* Функции pre-install используют `definePreInstallLogicFunction()` — та же специализированная конфигурация, что и у post-install, только привязанная к другому этапу жизненного цикла.
|
||||
* И обработчики pre-, и post-install получают один и тот же тип `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Импортируйте его один раз и используйте повторно в обоих хуках.
|
||||
* **Когда запускается хук**: выполняется непосредственно перед миграцией метаданных рабочего пространства (`synchronizeFromManifest`). Перед выполнением сервер запускает чисто добавочную «урезанную синхронизацию», которая регистрирует в метаданных рабочего пространства pre-install функцию **новой** версии — ничего больше не затрагивается — а затем выполняет её. Поскольку эта синхронизация только добавляет, объекты, поля и данные предыдущей версии остаются нетронутыми к моменту запуска вашего обработчика: вы можете безопасно читать и сохранять состояние до миграции.
|
||||
* **Модель выполнения**: pre-install выполняется **синхронно** и **блокирует установку**. Если обработчик генерирует исключение, установка прерывается до применения каких-либо изменений схемы — рабочее пространство остаётся на предыдущей версии в согласованном состоянии. Это сделано намеренно: pre-install — ваш последний шанс отказать в рискованном обновлении.
|
||||
* Как и в случае с post-install, для каждого приложения допускается только одна предустановочная функция. Она автоматически добавляется в манифест приложения в поле `preInstallLogicFunction` во время сборки.
|
||||
* **Не выполняется в режиме разработки**: как и post-install, процесс установки полностью пропускается для локально зарегистрированных приложений, поэтому pre-install никогда не запускается при `yarn twenty dev`. Используйте `yarn twenty exec --preInstall`, чтобы запустить это вручную.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Pre-install vs post-install: when to use which" description="Choosing the right install hook">
|
||||
<Accordion title="Pre-install и post-install: когда что использовать" description="Выбор подходящего хука установки">
|
||||
|
||||
Both hooks are part of the same install flow and receive the same `InstallPayload`. The difference is **when** they run relative to the workspace metadata migration, and that changes what data they can safely touch.
|
||||
Оба хука являются частью одного и того же процесса установки и получают один и тот же `InstallPayload`. Разница в том, **когда** они запускаются относительно миграции метаданных рабочего пространства, и это определяет, к каким данным можно безопасно обращаться.
|
||||
|
||||
Pre-install is always **synchronous** (it blocks the install and can abort it). Post-install is **asynchronous by default** — enqueued on a worker with automatic retries — but can opt into synchronous execution with `shouldRunSynchronously: true`. See the `definePostInstallLogicFunction` accordion above for when to use each mode.
|
||||
Pre-install всегда **синхронный** (он блокирует установку и может её прервать). Post-install **по умолчанию асинхронный** — ставится в очередь воркера с автоматическими повторами — но может перейти к синхронному выполнению с `shouldRunSynchronously: true`. См. аккордеон `definePostInstallLogicFunction` выше о том, когда использовать каждый режим.
|
||||
|
||||
**Use `post-install` for anything that needs the new schema to exist.** This is the common case:
|
||||
**Используйте `post-install` для всего, что требует наличия новой схемы.** Это распространённый случай:
|
||||
|
||||
* Seeding default data (creating initial records, default views, demo content) against newly-added objects and fields.
|
||||
* Registering webhooks with third-party services now that the app has its credentials.
|
||||
* Calling your own API to finish setup that depends on the synchronized metadata.
|
||||
* Idempotent "ensure this exists" logic that should reconcile state on every upgrade — combine with `shouldRunOnVersionUpgrade: true`.
|
||||
* Наполнение данными по умолчанию (создание начальных записей, стандартных представлений, демонстрационного контента) для недавно добавленных объектов и полей.
|
||||
* Регистрация вебхуков в сторонних сервисах теперь, когда у приложения уже есть учётные данные.
|
||||
* Вызов вашего собственного API для завершения настройки, зависящей от синхронизированных метаданных.
|
||||
* Идемпотентная логика «убедиться, что это существует», которая должна приводить состояние в соответствие при каждом обновлении — совместите с `shouldRunOnVersionUpgrade: true`.
|
||||
|
||||
Example — seed a default `PostCard` record after install:
|
||||
Пример — создать запись `PostCard` по умолчанию после установки:
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -137,14 +137,14 @@ export default definePostInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Use `pre-install` when a migration would otherwise destroy or corrupt existing data.** Because pre-install runs against the *previous* schema and its failure rolls back the upgrade, it is the right place for anything risky:
|
||||
**Используйте `pre-install`, когда миграция в противном случае уничтожит или повредит существующие данные.** Поскольку pre-install работает с *предыдущей* схемой и при сбое откатывает обновление, это правильное место для всего рискованного:
|
||||
|
||||
* **Backing up data that is about to be dropped or restructured** — e.g. you are removing a field in v2 and need to copy its values into another field or export them to storage before the migration runs.
|
||||
* **Archiving records that a new constraint would invalidate** — e.g. a field is becoming `NOT NULL` and you need to delete or fix rows with null values first.
|
||||
* **Validating compatibility and refusing the upgrade if the current data cannot be migrated cleanly** — throw from the handler and the install aborts with no changes applied. This is safer than discovering the incompatibility mid-migration.
|
||||
* **Renaming or rekeying data** ahead of a schema change that would lose the association.
|
||||
* **Резервное копирование данных, которые будут удалены или реструктурированы** — например, вы удаляете поле в v2 и вам нужно скопировать его значения в другое поле или экспортировать их в хранилище до запуска миграции.
|
||||
* **Архивирование записей, которые новое ограничение сделает недопустимыми** — например, поле становится `NOT NULL`, и вам сначала нужно удалить или исправить строки со значениями null.
|
||||
* **Проверка совместимости и отказ от обновления, если текущие данные нельзя корректно мигрировать** — выбросьте исключение из обработчика, и установка прервётся без внесения изменений. Это безопаснее, чем обнаружить несовместимость в середине миграции.
|
||||
* **Переименование или изменение ключей данных** перед изменением схемы, которое привело бы к потере связи.
|
||||
|
||||
Example — archive records before a destructive migration:
|
||||
Пример — архивировать записи перед разрушительной миграцией:
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
@@ -186,20 +186,20 @@ export default definePreInstallLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**Rule of thumb:**
|
||||
**Общее правило:**
|
||||
|
||||
| You want to... | Use |
|
||||
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| Seed default data, configure the workspace, register external resources | `post-install` |
|
||||
| Run long-running seeding or third-party calls that shouldn't block the install response | `post-install` (default — `shouldRunSynchronously: false`, with worker retries) |
|
||||
| Run fast setup that the caller will rely on immediately after the install call returns | `post-install` with `shouldRunSynchronously: true` |
|
||||
| Read or back up data that the upcoming migration would lose | `pre-install` |
|
||||
| Reject an upgrade that would corrupt existing data | `pre-install` (throw from the handler) |
|
||||
| Run reconciliation on every upgrade | `post-install` with `shouldRunOnVersionUpgrade: true` |
|
||||
| Do one-off setup on the first install only | `post-install` with `shouldRunOnVersionUpgrade: false` (default) |
|
||||
| Вы хотите... | Использовать |
|
||||
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| Наполнить данными по умолчанию, настроить рабочее пространство, зарегистрировать внешние ресурсы | `post-install` |
|
||||
| Выполнить длительное наполнение или сторонние вызовы, которые не должны блокировать ответ установки | `post-install` (по умолчанию — `shouldRunSynchronously: false`, с повторами воркера) |
|
||||
| Выполнить быструю настройку, на которую вызывающая сторона будет полагаться сразу после возврата вызова установки | `post-install` с `shouldRunSynchronously: true` |
|
||||
| Прочитать или сохранить данные, которые предстоящая миграция может потерять | `pre-install` |
|
||||
| Отклонить обновление, которое повредит существующие данные | `pre-install` (бросьте исключение из обработчика) |
|
||||
| Выполнять согласование при каждом обновлении | `post-install` с `shouldRunOnVersionUpgrade: true` |
|
||||
| Сделать одноразовую настройку только при первой установке | `post-install` с `shouldRunOnVersionUpgrade: false` (по умолчанию) |
|
||||
|
||||
<Note>
|
||||
If in doubt, default to **post-install**. Only reach for pre-install when the migration itself is destructive and you need to intercept the previous state before it is gone.
|
||||
Если сомневаетесь, выбирайте по умолчанию **post-install**. Обращайтесь к pre-install только тогда, когда сама миграция разрушительна и вам нужно перехватить предыдущее состояние, прежде чем оно исчезнет.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Configure the app itself — its identity, default permissions, and what runs at install time.
|
||||
title: Обзор
|
||||
description: Настройте само приложение — его идентичность, разрешения по умолчанию и то, что выполняется во время установки.
|
||||
icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
A Twenty app's **config layer** is what describes the app *to the platform* — its identity, the permissions it holds, and the code that runs during install or upgrade. These declarations don't add new data shapes or runtime behavior; they tell Twenty *who the app is* and *how to set it up*.
|
||||
**Конфигурационный слой** приложения Twenty описывает приложение *для платформы* — его идентичность, разрешения, которыми оно обладает, и код, который выполняется при установке или обновлении. Эти декларации не добавляют новых структур данных или поведения во время выполнения; они сообщают Twenty, *что это за приложение* и *как его настроить*.
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
@@ -26,26 +26,26 @@ A Twenty app's **config layer** is what describes the app *to the platform* —
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## В этом разделе
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Application Config" icon="rocket" href="/l/ru/developers/extend/apps/config/application">
|
||||
`defineApplication` — identity, default role, variables, marketplace metadata.
|
||||
<Card title="Конфигурация приложения" icon="rocket" href="/l/ru/developers/extend/apps/config/application">
|
||||
`defineApplication` — идентичность, роль по умолчанию, переменные, метаданные маркетплейса.
|
||||
</Card>
|
||||
<Card title="Roles & Permissions" icon="shield-halved" href="/l/ru/developers/extend/apps/config/roles">
|
||||
`defineRole` — declare what your app's logic functions can read and write.
|
||||
<Card title="Роли и разрешения" icon="shield-halved" href="/l/ru/developers/extend/apps/config/roles">
|
||||
`defineRole` — определите, что логические функции вашего приложения могут читать и записывать.
|
||||
</Card>
|
||||
<Card title="Install Hooks" icon="wrench" href="/l/ru/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` and `definePostInstallLogicFunction` — back up data, seed defaults, validate upgrades.
|
||||
<Card title="Хуки установки" icon="wrench" href="/l/ru/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` и `definePostInstallLogicFunction` — создавайте резервные копии данных, заполняйте значения по умолчанию, проверяйте обновления.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## How the pieces relate
|
||||
## Связь между частями
|
||||
|
||||
* **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).
|
||||
* **Приложение** — это точка входа. У каждого приложения есть ровно один вызов `defineApplication()`, и он указывает на одну **роль** как роль по умолчанию.
|
||||
* **Роль** управляет тем, что логические функции и фронтенд‑компоненты приложения могут читать и записывать. Следуйте принципу наименьших привилегий: выдавайте только те разрешения, которые вашему коду действительно нужны.
|
||||
* **Хуки установки** запускаются при установке или обновлении — предустановочный до миграции метаданных (чтобы можно было отклонить рискованное обновление), постустановочный после миграции (чтобы можно было заполнить данные по умолчанию в соответствии с новой схемой).
|
||||
|
||||
<Note>
|
||||
Install hooks share the [logic function](/l/ru/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).
|
||||
Хуки установки используют то же окружение выполнения, что и [логическая функция](/l/ru/developers/extend/apps/logic/logic-functions) — тот же формат обработчика, те же переменные окружения, тот же типизированный клиент API, — но объявляются через собственные функции `define` и находятся вне обычной модели триггеров (HTTP, cron, события базы данных).
|
||||
</Note>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
---
|
||||
title: Public Assets
|
||||
description: Ship static files — images, icons, fonts — alongside your app via the public/ folder.
|
||||
title: Публичные ресурсы
|
||||
description: Отправляйте статические файлы — изображения, иконки, шрифты — вместе с вашим приложением через папку public/.
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
The `public/` folder at the root of your app holds static files — images, icons, fonts, or any other assets your app needs at runtime. These files are automatically included in builds, synced during dev mode, and uploaded to the server.
|
||||
Папка `public/` в корне вашего приложения содержит статические файлы — изображения, значки, шрифты и любые другие ресурсы, необходимые вашему приложению во время выполнения. Эти файлы автоматически включаются в сборки, синхронизируются в режиме разработки и загружаются на сервер.
|
||||
|
||||
Files placed in `public/` are:
|
||||
Файлы, размещённые в `public/`, являются:
|
||||
|
||||
* **Publicly accessible** — once synced to the server, assets are served at a public URL. No authentication is needed to access them.
|
||||
* **Available in front components** — use asset URLs to display images, icons, or any media inside your React components.
|
||||
* **Available in logic functions** — reference asset URLs in emails, API responses, or any server-side logic.
|
||||
* **Used for marketplace metadata** — the `logoUrl` and `screenshots` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published.
|
||||
* **Auto-synced in dev mode** — when you add, update, or delete a file in `public/`, it is synced to the server automatically. No restart needed.
|
||||
* **Included in builds** — `yarn twenty build` bundles all public assets into the distribution output.
|
||||
* **Публично доступными** — после синхронизации с сервером ресурсы доступны по публичному URL. Для доступа к ним аутентификация не требуется.
|
||||
* **Доступными в компонентах фронтенда** — используйте URL ресурсов для отображения изображений, значков или любого медиа внутри ваших компонентов React.
|
||||
* **Доступными в логических функциях** — используйте URL ресурсов в письмах, ответах API или любой серверной логике.
|
||||
* **Используются для метаданных маркетплейса** — поля `logoUrl` и `screenshots` в `defineApplication()` ссылаются на файлы из этой папки (например, `public/logo.png`). Они отображаются в маркетплейсе при публикации вашего приложения.
|
||||
* **Автосинхронизация в режиме разработки** — когда вы добавляете, обновляете или удаляете файл в `public/`, он автоматически синхронизируется с сервером. Перезапуск не требуется.
|
||||
* **Включены в сборки** — `yarn twenty build` упаковывает все публичные ресурсы в выходной дистрибутив.
|
||||
|
||||
## Accessing public assets with `getPublicAssetUrl`
|
||||
## Доступ к публичным ресурсам с помощью `getPublicAssetUrl`
|
||||
|
||||
Use the `getPublicAssetUrl` helper from `twenty-sdk` to get the full URL of a file in your `public/` directory. It works in both **logic functions** and **front components**.
|
||||
Используйте хелпер `getPublicAssetUrl` из `twenty-sdk`, чтобы получить полный URL файла в каталоге `public/` вашего приложения. Он работает как в **логических функциях**, так и в **компонентах фронтенда**.
|
||||
|
||||
**In a logic function:**
|
||||
**В логической функции:**
|
||||
|
||||
```ts src/logic-functions/send-invoice.ts
|
||||
import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -44,7 +44,7 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
**In a front component:**
|
||||
**В компоненте фронтенда:**
|
||||
|
||||
```tsx src/front-components/company-card.tsx
|
||||
import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define';
|
||||
@@ -56,4 +56,4 @@ export default defineFrontComponent(() => {
|
||||
});
|
||||
```
|
||||
|
||||
The `path` argument is relative to your app's `public/` folder. Both `getPublicAssetUrl('logo.png')` and `getPublicAssetUrl('public/logo.png')` resolve to the same URL — the `public/` prefix is stripped automatically if present.
|
||||
Аргумент `path` задаётся относительно папки `public/` вашего приложения. И `getPublicAssetUrl('logo.png')`, и `getPublicAssetUrl('public/logo.png')` приводят к одному и тому же URL — префикс `public/`, если он есть, удаляется автоматически.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Roles & Permissions
|
||||
description: Declare what objects and fields your app's logic functions and front components can read and write.
|
||||
title: Роли и разрешения
|
||||
description: Укажите, к каким объектам и полям логические функции и фронтенд‑компоненты вашего приложения могут выполнять чтение и запись.
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
A **role** is a permission set: which objects an app can read or write, which fields it can see, and which platform-level capabilities it can use. Every app's logic functions and front components inherit the permissions of the role declared as `defaultRoleUniversalIdentifier` in [`defineApplication`](/l/ru/developers/extend/apps/config/application).
|
||||
**Роль** — это набор разрешений: какие объекты приложение может читать или изменять, какие поля оно может видеть и какие возможности платформенного уровня оно может использовать. Все логические функции и фронтенд‑компоненты каждого приложения наследуют разрешения роли, объявленной как `defaultRoleUniversalIdentifier` в [`defineApplication`](/l/ru/developers/extend/apps/config/application).
|
||||
|
||||
```ts src/roles/restricted-company-role.ts
|
||||
import {
|
||||
@@ -49,9 +49,9 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
## The default function role
|
||||
## Роль функции по умолчанию
|
||||
|
||||
When you scaffold a new app, the CLI creates a default role file:
|
||||
Когда вы генерируете новое приложение, CLI создаёт файл роли по умолчанию:
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineRole, PermissionFlag } from 'twenty-sdk/define';
|
||||
@@ -77,14 +77,14 @@ export default defineRole({
|
||||
});
|
||||
```
|
||||
|
||||
This role's `universalIdentifier` is referenced from `application-config.ts` as `defaultRoleUniversalIdentifier`:
|
||||
Значение `universalIdentifier` этой роли указывается в `application-config.ts` как `defaultRoleUniversalIdentifier`:
|
||||
|
||||
* **`*.role.ts`** declares what the role can do.
|
||||
* **`application-config.ts`** points to that role so your functions inherit its permissions.
|
||||
* **`*.role.ts`** определяет, что может делать роль.
|
||||
* **`application-config.ts`** указывает на эту роль, чтобы ваши функции наследовали её права.
|
||||
|
||||
## Best practices
|
||||
## Лучшие практики
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it — the default grants broad read access, which is rarely what you want in production.
|
||||
* Replace `objectPermissions` and `fieldPermissions` with the exact objects and fields your functions actually need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal.
|
||||
* See a working example: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
* Начните с сгенерированной роли и постепенно ограничивайте её — роль по умолчанию предоставляет широкий доступ на чтение, что редко подходит для продакшена.
|
||||
* Замените `objectPermissions` и `fieldPermissions` на точные объекты и поля, которые действительно нужны вашим функциям.
|
||||
* `permissionFlags` управляют доступом к возможностям на уровне платформы. Сведите их к минимуму.
|
||||
* См. рабочий пример: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Extending Objects
|
||||
description: Add fields to standard Twenty objects (Person, Company, …) or to objects from other apps using defineField.
|
||||
title: Расширение объектов
|
||||
description: Добавляйте поля к стандартным объектам Twenty (Person, Company, …) или к объектам из других приложений с помощью `defineField`.
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
Use `defineField()` to add a field to an object you don't own — a standard Twenty object like Person or Company, or an object shipped by another installed app. Unlike inline fields declared inside [`defineObject`](/l/ru/developers/extend/apps/data/objects), standalone fields require an `objectUniversalIdentifier` to specify which object they extend.
|
||||
Используйте `defineField()` для добавления поля к объекту, которым вы не владеете — стандартному объекту Twenty, такому как Person или Company, или объекту, поставляемому другим установленным приложением. В отличие от встроенных полей, объявленных внутри [`defineObject`](/l/ru/developers/extend/apps/data/objects), отдельные поля требуют `objectUniversalIdentifier`, чтобы указать, какой объект они расширяют.
|
||||
|
||||
```ts src/fields/company-loyalty-tier.field.ts
|
||||
import { defineField, FieldType } from 'twenty-sdk/define';
|
||||
@@ -24,9 +24,9 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Key points
|
||||
## Основные моменты
|
||||
|
||||
* `objectUniversalIdentifier` identifies the target object. For standard Twenty objects, import the constant from `twenty-sdk`:
|
||||
* `objectUniversalIdentifier` определяет целевой объект. Для стандартных объектов Twenty импортируйте константу из `twenty-sdk`:
|
||||
|
||||
```ts
|
||||
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
@@ -37,12 +37,12 @@ export default defineField({
|
||||
// …
|
||||
```
|
||||
|
||||
* When defining fields **inline inside `defineObject()`**, you do **not** need `objectUniversalIdentifier` — it's inherited from the parent object.
|
||||
* При определении полей **непосредственно внутри `defineObject()`** вам **не** нужен `objectUniversalIdentifier` — он наследуется от родительского объекта.
|
||||
|
||||
* `defineField()` is the only way to add fields to objects you didn't create with `defineObject()`.
|
||||
* `defineField()` — единственный способ добавить поля к объектам, которые вы не создавали с помощью `defineObject()`.
|
||||
|
||||
* File location is up to you. The convention is `src/fields/\<name>.field.ts`, but the SDK detects fields anywhere in `src/`.
|
||||
* Расположение файла зависит от вас. Принятое соглашение — `src/fields/\<name>.field.ts`, но SDK обнаруживает поля в любом месте внутри `src/`.
|
||||
|
||||
## Adding a relation to an existing object
|
||||
## Добавление связи к существующему объекту
|
||||
|
||||
To add a relation field (e.g. linking your custom object to a standard `Person`), use `defineField()` with `FieldType.RELATION`. The pattern is the same as for inline relations but with `objectUniversalIdentifier` set explicitly. See [Relations](/l/ru/developers/extend/apps/data/relations) for the bidirectional pattern.
|
||||
Чтобы добавить поле связи (например, связать ваш пользовательский объект со стандартным `Person`), используйте `defineField()` с `FieldType.RELATION`. Шаблон тот же, что и для встроенных связей, но с явным указанием `objectUniversalIdentifier`. Смотрите раздел [Relations](/l/ru/developers/extend/apps/data/relations) для двунаправленного шаблона.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Объекты
|
||||
description: Declare new record types — custom tables with their own fields — using defineObject.
|
||||
description: Объявляйте новые типы записей — пользовательские таблицы с собственными полями — с помощью defineObject.
|
||||
icon: таблица
|
||||
---
|
||||
|
||||
Custom **objects** are new record types your app adds to a workspace — Post Card, Invoice, Subscription, anything specific to your domain. Each object declares its schema (fields, relations, default values) and a stable universal identifier that survives across syncs and deploys.
|
||||
Пользовательские **объекты** — это новые типы записей, которые ваше приложение добавляет в рабочее пространство — открытка, счёт-фактура, подписка, что‑то специфичное для вашей предметной области. Каждый объект объявляет свою схему (поля, связи, значения по умолчанию) и стабильный универсальный идентификатор, который сохраняется между синхронизациями и деплоями.
|
||||
|
||||
```ts src/objects/post-card.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
@@ -79,15 +79,15 @@ export default defineObject({
|
||||
* `universalIdentifier` должен быть уникальным и стабильным между развёртываниями.
|
||||
* Каждому полю требуются `name`, `type`, `label` и собственный стабильный `universalIdentifier`.
|
||||
* Массив `fields` необязателен — вы можете определять объекты без пользовательских полей.
|
||||
* Inline fields defined here do **not** need an `objectUniversalIdentifier` — it's inherited from the parent object. Use [`defineField()`](/l/ru/developers/extend/apps/data/extending-objects) to add fields to objects you don't own.
|
||||
* You can scaffold new objects with `yarn twenty add object`, which guides you through naming, fields, and relationships. See [Architecture → Scaffolding entities](/l/ru/developers/extend/apps/getting-started/scaffolding).
|
||||
* Встроенным полям, определённым здесь, **не** нужен `objectUniversalIdentifier` — он наследуется от родительского объекта. Используйте [`defineField()`](/l/ru/developers/extend/apps/data/extending-objects), чтобы добавить поля к объектам, которые вам не принадлежат.
|
||||
* Вы можете сгенерировать новые объекты с помощью `yarn twenty add object`, который проведёт вас через выбор именования, полей и связей. См. [Architecture → Scaffolding entities](/l/ru/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
<Note>
|
||||
**Base fields are added automatically.** When you define a custom object, Twenty creates standard fields like `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, and `deletedAt` for you. You don't need to declare them in your `fields` array — only your custom fields. You can override a default field by declaring one with the same name, but this is rarely a good idea.
|
||||
**Базовые поля добавляются автоматически.** Когда вы определяете пользовательский объект, Twenty создаёт для вас стандартные поля, такие как `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` и `deletedAt`. Вам не нужно объявлять их в массиве `fields` — только ваши пользовательские поля. Вы можете переопределить базовое поле, объявив поле с тем же именем, но это редко бывает хорошей идеей.
|
||||
</Note>
|
||||
|
||||
## Что дальше
|
||||
|
||||
* **Connect this object to others** — see [Relations](/l/ru/developers/extend/apps/data/relations) for the bidirectional relation pattern.
|
||||
* **Add fields to objects from other apps** — see [Extending Objects](/l/ru/developers/extend/apps/data/extending-objects) for `defineField()`.
|
||||
* **Display this object in the UI** — see [Views](/l/ru/developers/extend/apps/layout/views) and [Navigation Menu Items](/l/ru/developers/extend/apps/layout/navigation-menu-items) to put it in the sidebar.
|
||||
* **Свяжите этот объект с другими** — см. [Relations](/l/ru/developers/extend/apps/data/relations) для двунаправленного шаблона связей.
|
||||
* **Добавляйте поля к объектам из других приложений** — см. [Extending Objects](/l/ru/developers/extend/apps/data/extending-objects) по `defineField()`.
|
||||
* **Отобразите этот объект в интерфейсе** — см. [Views](/l/ru/developers/extend/apps/layout/views) и [Navigation Menu Items](/l/ru/developers/extend/apps/layout/navigation-menu-items), чтобы поместить его в боковую панель.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Shape the data your app adds to a workspace — objects, fields, and relations.
|
||||
title: Обзор
|
||||
description: Определяйте структуру данных, которые ваше приложение добавляет в рабочее пространство — объекты, поля и связи.
|
||||
icon: database
|
||||
---
|
||||
|
||||
A Twenty app's **data layer** is the data your app *adds* to a workspace — the new record types it declares, the columns it adds to existing objects, and how those records connect to each other.
|
||||
**Уровень данных** приложения Twenty — это данные, которые ваше приложение *добавляет* в рабочее пространство: новые типы записей, которые оно объявляет, столбцы, которые оно добавляет к существующим объектам, и то, как эти записи связываются друг с другом.
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────┐
|
||||
@@ -23,30 +23,30 @@ A Twenty app's **data layer** is the data your app *adds* to a workspace — the
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## In this section
|
||||
## В этом разделе
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Objects" icon="table" href="/l/ru/developers/extend/apps/data/objects">
|
||||
`defineObject` — declare new record types with their own fields.
|
||||
<Card title="Объекты" icon="таблица" href="/l/ru/developers/extend/apps/data/objects">
|
||||
`defineObject` — объявляйте новые типы записей с их собственными полями.
|
||||
</Card>
|
||||
<Card title="Extending Objects" icon="wand-magic-sparkles" href="/l/ru/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — add fields to standard or other apps' objects.
|
||||
<Card title="Расширение объектов" icon="wand-magic-sparkles" href="/l/ru/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — добавляйте поля к стандартным объектам или объектам других приложений.
|
||||
</Card>
|
||||
<Card title="Relations" icon="diagram-project" href="/l/ru/developers/extend/apps/data/relations">
|
||||
Bidirectional `MANY_TO_ONE` / `ONE_TO_MANY` connections between objects.
|
||||
<Card title="Связи" icon="diagram-project" href="/l/ru/developers/extend/apps/data/relations">
|
||||
Двусторонние связи `MANY_TO_ONE` / `ONE_TO_MANY` между объектами.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Entities at a glance
|
||||
## Сущности одним взглядом
|
||||
|
||||
| Entity | Purpose | Defined with |
|
||||
| ------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| **Object** | A new custom record type (e.g. PostCard, Invoice) with its own fields | `defineObject()` |
|
||||
| **Field** | A column on an object. Standalone fields can extend objects you didn't create (e.g. add `loyaltyTier` to Company) | `defineField()` |
|
||||
| **Relation** | A bidirectional link between two objects — both sides declared as fields | `defineField()` with `FieldType.RELATION` |
|
||||
| Сущность | Назначение | Определяется с помощью |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
| **Объект** | Новый пользовательский тип записей (например, PostCard, Invoice) с собственными полями | `defineObject()` |
|
||||
| **Поле** | Столбец в объекте. Отдельные поля могут расширять объекты, которые вы не создавали (например, добавить `loyaltyTier` к Company) | `defineField()` |
|
||||
| **Связь** | Двусторонняя связь между двумя объектами — обе стороны объявлены как поля | `defineField()` с `FieldType.RELATION` |
|
||||
|
||||
The SDK detects these via AST analysis at build time, so file organization is up to you — the convention is `src/objects/` and `src/fields/`. Stable `universalIdentifier` UUIDs tie everything together across deploys.
|
||||
SDK обнаруживает их с помощью анализа AST во время сборки, поэтому организация файлов остается на ваше усмотрение — по соглашению используются `src/objects/` и `src/fields/`. Стабильные UUID `universalIdentifier` связывают все воедино между развертываниями.
|
||||
|
||||
<Note>
|
||||
Looking for **Application Config** or **Roles & Permissions**? Those describe the app itself rather than the data it adds — they live under [Config](/l/ru/developers/extend/apps/config/overview). Looking for **Connections** (Linear, GitHub, Slack OAuth)? Those exist to be called *from* logic functions and live under [Logic](/l/ru/developers/extend/apps/logic/connections).
|
||||
Ищете **Application Config** или **Roles & Permissions**? Они описывают само приложение, а не данные, которые оно добавляет, — их можно найти в разделе [Config](/l/ru/developers/extend/apps/config/overview). Ищете **Connections** (Linear, GitHub, Slack OAuth)? Они существуют для вызова *из* логических функций и находятся в разделе [Logic](/l/ru/developers/extend/apps/logic/connections).
|
||||
</Note>
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
title: Связи
|
||||
description: Connect objects together with bidirectional MANY_TO_ONE / ONE_TO_MANY relations.
|
||||
description: Связывайте объекты между собой двунаправленными связями MANY_TO_ONE / ONE_TO_MANY.
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
Relations connect two objects together. In Twenty, relations are always **bidirectional** — every relation has two sides, and each side is declared as a field that references the other.
|
||||
Отношения связывают два объекта между собой. В Twenty отношения всегда **двунаправленные** — у каждого отношения есть две стороны, и каждая сторона объявляется как поле, ссылающееся на другую.
|
||||
|
||||
| Тип отношения | Описание | Есть внешний ключ? |
|
||||
| ------------- | --------------------------------------------------------------------- | --------------------- |
|
||||
| `MANY_TO_ONE` | Многие записи этого объекта указывают на одну запись целевого объекта | Да (`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | Одна запись этого объекта имеет много записей целевого объекта | No (the inverse side) |
|
||||
| Тип отношения | Описание | Есть внешний ключ? |
|
||||
| ------------- | --------------------------------------------------------------------- | ---------------------- |
|
||||
| `MANY_TO_ONE` | Многие записи этого объекта указывают на одну запись целевого объекта | Да (`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | Одна запись этого объекта имеет много записей целевого объекта | Нет (обратная сторона) |
|
||||
|
||||
## How relations work
|
||||
## Как работают отношения
|
||||
|
||||
Every relation requires **two fields** that reference each other:
|
||||
Каждое отношение требует **двух полей**, которые ссылаются друг на друга:
|
||||
|
||||
1. The **MANY_TO_ONE** side — lives on the object that holds the foreign key.
|
||||
2. The **ONE_TO_MANY** side — lives on the object that owns the collection.
|
||||
1. Сторона **MANY_TO_ONE** — находится в объекте, который содержит внешний ключ.
|
||||
2. Сторона **ONE_TO_MANY** — находится в объекте, которому принадлежит коллекция.
|
||||
|
||||
Both fields use `FieldType.RELATION` and cross-reference each other via `relationTargetFieldMetadataUniversalIdentifier`.
|
||||
Оба поля используют `FieldType.RELATION` и ссылаются друг на друга через `relationTargetFieldMetadataUniversalIdentifier`.
|
||||
|
||||
## Example: Post Card has many Recipients
|
||||
## Пример: Почтовая открытка имеет много получателей
|
||||
|
||||
A `PostCard` can be sent to many `PostCardRecipient` records. Each recipient belongs to exactly one post card.
|
||||
`PostCard` может быть отправлен множеству записей `PostCardRecipient`. Каждый получатель относится ровно к одной открытке.
|
||||
|
||||
**Step 1: Define the ONE_TO_MANY side on PostCard** (the "one" side):
|
||||
**Шаг 1: Определите сторону ONE_TO_MANY на PostCard** (сторона "one"):
|
||||
|
||||
```ts src/fields/post-card-recipients-on-post-card.field.ts
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
|
||||
@@ -51,7 +51,7 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Define the MANY_TO_ONE side on PostCardRecipient** (the "many" side — holds the foreign key):
|
||||
**Шаг 2: Определите сторону MANY_TO_ONE на PostCardRecipient** (сторона "many" — содержит внешний ключ):
|
||||
|
||||
```ts src/fields/post-card-on-post-card-recipient.field.ts
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
|
||||
@@ -81,12 +81,12 @@ export default defineField({
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Circular imports:** both relation fields reference each other's `universalIdentifier`. To avoid circular import issues, export your field IDs as named constants from each file and import them in the other. The build system resolves these at compile time.
|
||||
**Циклические импорты:** оба поля отношений ссылаются на `universalIdentifier` друг друга. Чтобы избежать проблем с циклическими импортами, экспортируйте идентификаторы полей как именованные константы из каждого файла и импортируйте их в другом. Система сборки разрешает это на этапе компиляции.
|
||||
</Note>
|
||||
|
||||
## Relating to standard objects
|
||||
## Связывание со стандартными объектами
|
||||
|
||||
To create a relation with a built-in Twenty object (Person, Company, etc.), use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
Чтобы создать отношение со встроенным объектом Twenty (Person, Company и т. д.), используйте `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`:
|
||||
|
||||
```ts src/fields/person-on-self-hosting-user.field.ts
|
||||
import {
|
||||
@@ -120,20 +120,20 @@ export default defineField({
|
||||
});
|
||||
```
|
||||
|
||||
## Relation field properties
|
||||
## Свойства поля отношения
|
||||
|
||||
| Property | Required | Description |
|
||||
| ------------------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `type` | Yes | Must be `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the target object |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Yes | The `universalIdentifier` of the matching field on the target object |
|
||||
| `universalSettings.relationType` | Yes | `RelationType.MANY_TO_ONE` or `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | MANY_TO_ONE only | What happens when the referenced record is deleted: `CASCADE`, `SET_NULL`, `RESTRICT`, or `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | MANY_TO_ONE only | Database column name for the foreign key (e.g., `postCardId`) |
|
||||
| Свойство | Обязательно | Описание |
|
||||
| ------------------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `type` | Да | Должно быть `FieldType.RELATION` |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | Да | `universalIdentifier` целевого объекта |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | Да | `universalIdentifier` соответствующего поля на целевом объекте |
|
||||
| `universalSettings.relationType` | Да | `RelationType.MANY_TO_ONE` или `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | Только для MANY_TO_ONE | Что происходит при удалении связанной записи: `CASCADE`, `SET_NULL`, `RESTRICT` или `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | Только для MANY_TO_ONE | Имя столбца базы данных для внешнего ключа (например, `postCardId`) |
|
||||
|
||||
## Inline relation fields
|
||||
## Встроенные поля связей
|
||||
|
||||
You can also declare a relation directly inside [`defineObject`](/l/ru/developers/extend/apps/data/objects). When inline, omit `objectUniversalIdentifier` — it's inherited from the parent object:
|
||||
Вы также можете объявить связь напрямую внутри [`defineObject`](/l/ru/developers/extend/apps/data/objects). При встроенном объявлении опустите `objectUniversalIdentifier` — он наследуется от родительского объекта:
|
||||
|
||||
```ts
|
||||
export default defineObject({
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: How Twenty apps work — entity model, sandboxing, and the install lifecycle.
|
||||
title: Концепции
|
||||
description: Как работают приложения Twenty — модель сущностей, песочницы и жизненный цикл установки.
|
||||
icon: sitemap
|
||||
---
|
||||
|
||||
Twenty apps are TypeScript packages that extend your workspace with custom objects, logic, UI components, and AI capabilities. They run on the Twenty platform with full sandboxing and permission controls.
|
||||
Приложения Twenty — это пакеты TypeScript, которые расширяют ваше рабочее пространство пользовательскими объектами, логикой, компонентами интерфейса и возможностями ИИ. Они работают на платформе Twenty с полной изоляцией в песочнице и контролем прав доступа.
|
||||
|
||||
## How apps work
|
||||
## Как работают приложения
|
||||
|
||||
An app is a collection of **entities** declared using `defineEntity()` functions from the `twenty-sdk` package. The SDK detects these declarations via AST analysis at build time and produces a **manifest** — a complete description of what your app adds to a workspace. These functions validate your configuration at build time and provide IDE autocompletion and type safety.
|
||||
Приложение — это набор **сущностей**, объявленных с помощью функций `defineEntity()` из пакета `twenty-sdk`. SDK обнаруживает эти объявления посредством анализа AST на этапе сборки и формирует **манифест** — полное описание того, что ваше приложение добавляет в рабочее пространство. Эти функции проверяют вашу конфигурацию на этапе сборки и обеспечивают автодополнение в IDE и безопасность типов.
|
||||
|
||||
```
|
||||
your-app/
|
||||
@@ -29,35 +29,35 @@ your-app/
|
||||
```
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** Entity detection is AST-based — the SDK finds `export default defineEntity(...)` calls regardless of where the file lives. The folder structure above is a convention, not a requirement.
|
||||
**Организация файлов — на ваше усмотрение.** Обнаружение сущностей основано на AST — SDK находит вызовы `export default defineEntity(...)` независимо от расположения файла. Структура папок выше — это соглашение, а не требование.
|
||||
</Note>
|
||||
|
||||
## Entity types
|
||||
## Типы сущностей
|
||||
|
||||
| Entity | Purpose | Docs |
|
||||
| ------------------------ | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| **Application** | App identity, default role, variables | [Application Config](/l/ru/developers/extend/apps/config/application) |
|
||||
| **Role** | Permission sets on objects and fields | [Roles & Permissions](/l/ru/developers/extend/apps/config/roles) |
|
||||
| **Object** | Custom record types with fields | [Objects](/l/ru/developers/extend/apps/data/objects) |
|
||||
| **Field** | Add fields to objects from other apps | [Extending Objects](/l/ru/developers/extend/apps/data/extending-objects) |
|
||||
| **Relation** | Bidirectional links between objects | [Relations](/l/ru/developers/extend/apps/data/relations) |
|
||||
| **Logic Function** | Server-side TypeScript with triggers | [Logic Functions](/l/ru/developers/extend/apps/logic/logic-functions) |
|
||||
| **Skill** | Reusable AI agent instructions | [Skills & Agents](/l/ru/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Agent** | AI assistants with custom prompts | [Skills & Agents](/l/ru/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Connection Provider** | OAuth credentials for third-party APIs | [Connections](/l/ru/developers/extend/apps/logic/connections) |
|
||||
| **View** | Pre-configured record list views | [Views](/l/ru/developers/extend/apps/layout/views) |
|
||||
| **Navigation Menu Item** | Custom sidebar entries | [Navigation Menu Items](/l/ru/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Page Layout** | Tabs and widgets on a record's detail page | [Page Layouts](/l/ru/developers/extend/apps/layout/page-layouts) |
|
||||
| **Front Component** | Sandboxed React UI inside Twenty | [Front Components](/l/ru/developers/extend/apps/layout/front-components) |
|
||||
| **Command Menu Item** | Quick actions and Cmd+K entries | [Command Menu Items](/l/ru/developers/extend/apps/layout/command-menu-items) |
|
||||
| Сущность | Назначение | Документация |
|
||||
| ------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| **Приложение** | Идентификация приложения, роль по умолчанию, переменные | [Конфигурация приложения](/l/ru/developers/extend/apps/config/application) |
|
||||
| **Роль** | Наборы прав для объектов и полей | [Роли и права доступа](/l/ru/developers/extend/apps/config/roles) |
|
||||
| **Объект** | Пользовательские типы записей с полями | [Объекты](/l/ru/developers/extend/apps/data/objects) |
|
||||
| **Поле** | Добавляйте поля к объектам из других приложений | [Расширение объектов](/l/ru/developers/extend/apps/data/extending-objects) |
|
||||
| **Связь** | Двунаправленные связи между объектами | [Связи](/l/ru/developers/extend/apps/data/relations) |
|
||||
| **Логическая функция** | Серверный TypeScript с триггерами | [Логические функции](/l/ru/developers/extend/apps/logic/logic-functions) |
|
||||
| **Навык** | Повторно используемые инструкции для ИИ-агента | [Навыки и агенты](/l/ru/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Агент** | ИИ-агенты с пользовательскими промптами | [Навыки и агенты](/l/ru/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **Провайдер подключения** | OAuth-учетные данные для сторонних API | [Подключения](/l/ru/developers/extend/apps/logic/connections) |
|
||||
| **Представление** | Преднастроенные представления списков записей | [Представления](/l/ru/developers/extend/apps/layout/views) |
|
||||
| **Пункт меню навигации** | Пользовательские элементы боковой панели | [Элементы меню навигации](/l/ru/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **Макет страницы** | Вкладки и виджеты на странице сведений о записи | [Макеты страниц](/l/ru/developers/extend/apps/layout/page-layouts) |
|
||||
| **Компонент фронтенда** | Изолированный в песочнице интерфейс React внутри Twenty | [Компоненты фронтенда](/l/ru/developers/extend/apps/layout/front-components) |
|
||||
| **Элемент меню команд** | Быстрые действия и элементы Cmd+K | [Элементы меню команд](/l/ru/developers/extend/apps/layout/command-menu-items) |
|
||||
|
||||
## Sandboxing
|
||||
## Изоляция в песочнице
|
||||
|
||||
* **Logic functions** run in isolated Node.js processes on the server. They only access data through the typed API client, scoped to the app's role permissions.
|
||||
* **Front components** run in Web Workers using Remote DOM — sandboxed from the main page but rendering native DOM elements (not iframes). They communicate with Twenty via a message-passing host API.
|
||||
* **Permissions** are enforced at the API level. The runtime token (`TWENTY_APP_ACCESS_TOKEN`) is derived from the role defined in `defineApplication()`.
|
||||
* **Логические функции** выполняются в изолированных процессах Node.js на сервере. Они получают доступ к данным только через типизированный клиент API, ограниченный правами роли приложения.
|
||||
* **Компоненты фронтенда** запускаются в Web Workers с использованием Remote DOM — изолированы от основной страницы, но при этом рендерят нативные элементы DOM (не iframes). Они взаимодействуют с Twenty через хостовый API обмена сообщениями.
|
||||
* **Права доступа** применяются на уровне API. Токен времени выполнения (`TWENTY_APP_ACCESS_TOKEN`) выводится из роли, определённой в `defineApplication()`.
|
||||
|
||||
## App lifecycle
|
||||
## Жизненный цикл приложения
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
@@ -76,26 +76,26 @@ 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 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](/l/ru/developers/extend/apps/config/install-hooks) for details.
|
||||
* **`yarn twenty dev`** — следит за исходными файлами и синхронизирует изменения в реальном времени с подключённым сервером Twenty. Типизированный клиент API автоматически пересоздаётся при изменении схемы.
|
||||
* **`yarn twenty build`** — компилирует TypeScript, упаковывает логические функции и фронтенд-компоненты с помощью esbuild и формирует манифест.
|
||||
* **Хуки до/после установки** — необязательные функции, которые выполняются во время установки. См. раздел [Install Hooks](/l/ru/developers/extend/apps/config/install-hooks) для подробностей.
|
||||
|
||||
## Next steps
|
||||
## Следующие шаги
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/ru/developers/extend/apps/config/overview">
|
||||
Application identity, default role, and install hooks.
|
||||
<Card title="Конфигурация" icon="screwdriver-wrench" href="/l/ru/developers/extend/apps/config/overview">
|
||||
Идентификация приложения, роль по умолчанию и хуки установки.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/ru/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
<Card title="Данные" icon="database" href="/l/ru/developers/extend/apps/data/overview">
|
||||
Объекты, поля и двунаправленные связи.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/ru/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Логика" icon="bolt" href="/l/ru/developers/extend/apps/logic/overview">
|
||||
Логические функции, навыки, агенты и OAuth-подключения.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/ru/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
<Card title="Макет" icon="table-columns" href="/l/ru/developers/extend/apps/layout/overview">
|
||||
Представления, навигация, макеты страниц, фронтенд-компоненты.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/ru/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Операции" icon="rocket" href="/l/ru/developers/extend/apps/operations/overview">
|
||||
CLI, тестирование, удаленные окружения, CI и публикация вашего приложения.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
---
|
||||
title: Local Server
|
||||
description: Manage the local Twenty Docker server — start, stop, upgrade, parallel test instance, and manual SDK setup.
|
||||
title: Локальный сервер
|
||||
description: Управление локальным сервером Twenty Docker — запуск, остановка, обновление, параллельный тестовый экземпляр и ручная настройка SDK.
|
||||
icon: server
|
||||
---
|
||||
|
||||
## Managing the local server
|
||||
## Управление локальным сервером
|
||||
|
||||
Use `yarn twenty server` to control the local Twenty container:
|
||||
Используйте `yarn twenty server` для управления локальным контейнером Twenty:
|
||||
|
||||
| Command | What it does |
|
||||
| -------------------------------------- | -------------------------------------------- |
|
||||
| `yarn twenty server start` | Start the server (pulls the image if needed) |
|
||||
| `yarn twenty server start --port 3030` | Start on a custom port |
|
||||
| `yarn twenty server stop` | Stop the server (preserves data) |
|
||||
| `yarn twenty server status` | Show URL, version, and login credentials |
|
||||
| `yarn twenty server logs` | Stream server logs |
|
||||
| `yarn twenty server reset` | Wipe data and start fresh |
|
||||
| `yarn twenty server upgrade` | Pull the latest `twenty-app-dev` image |
|
||||
| `yarn twenty server upgrade 2.2.0` | Upgrade to a specific version |
|
||||
| Команда | Что делает |
|
||||
| -------------------------------------- | ---------------------------------------------------- |
|
||||
| `yarn twenty server start` | Запустить сервер (при необходимости скачивает образ) |
|
||||
| `yarn twenty server start --port 3030` | Запустить на пользовательском порту |
|
||||
| `yarn twenty server stop` | Остановить сервер (данные сохраняются) |
|
||||
| `yarn twenty server status` | Показать URL, версию и учётные данные для входа |
|
||||
| `yarn twenty server logs` | Потоковый вывод журналов сервера |
|
||||
| `yarn twenty server reset` | Стереть данные и начать заново |
|
||||
| `yarn twenty server upgrade` | Скачать последний образ `twenty-app-dev` |
|
||||
| `yarn twenty server upgrade 2.2.0` | Обновить до конкретной версии |
|
||||
|
||||
Data persists across restarts in two Docker volumes (`twenty-app-dev-data` for PostgreSQL, `twenty-app-dev-storage` for files). Use `reset` to wipe everything.
|
||||
Данные сохраняются между перезапусками в двух томах Docker (`twenty-app-dev-data` для PostgreSQL, `twenty-app-dev-storage` для файлов). Используйте `reset`, чтобы стереть всё.
|
||||
|
||||
## Upgrading the server image
|
||||
## Обновление образа сервера
|
||||
|
||||
`yarn twenty server upgrade` pulls the latest image, compares digests, and only recreates the container if anything actually changed. Volumes are preserved — only the container is replaced. If a new image was pulled and the container was running, the upgrade automatically starts a new container; run `yarn twenty server start` afterward to wait for it to become healthy.
|
||||
`yarn twenty server upgrade` скачивает последний образ, сравнивает дайджесты и пересоздаёт контейнер только если действительно что-то изменилось. Ваши тома данных сохраняются — заменяется только контейнер. Если был скачан новый образ и контейнер работал, при обновлении автоматически запускается новый контейнер; затем выполните `yarn twenty server start`, чтобы дождаться его готовности.
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty server upgrade # Latest
|
||||
yarn twenty server upgrade 2.2.0 # Specific version
|
||||
```
|
||||
|
||||
Verify the running version with `yarn twenty server status` (it shows the `APP_VERSION` baked into the container).
|
||||
Проверьте запущенную версию с помощью `yarn twenty server status` (эта команда показывает `APP_VERSION`, встроенную в контейнер).
|
||||
|
||||
## Running a parallel test instance
|
||||
## Запуск параллельного тестового экземпляра
|
||||
|
||||
Pass `--test` to any `server` command to manage a second, fully isolated instance — useful for integration tests or experiments without touching your main dev data:
|
||||
Передайте `--test` любой команде `server`, чтобы управлять вторым, полностью изолированным экземпляром — это полезно для запуска интеграционных тестов или экспериментов, не затрагивая ваши основные данные разработки.
|
||||
|
||||
| Command | What it does |
|
||||
| ----------------------------------- | ----------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Start the test instance (defaults to port 2021) |
|
||||
| `yarn twenty server stop --test` | Stop it |
|
||||
| `yarn twenty server status --test` | Show its status |
|
||||
| `yarn twenty server logs --test` | Stream its logs |
|
||||
| `yarn twenty server reset --test` | Wipe its data |
|
||||
| `yarn twenty server upgrade --test` | Upgrade its image |
|
||||
| Команда | Что делает |
|
||||
| ----------------------------------- | ------------------------------------------------------- |
|
||||
| `yarn twenty server start --test` | Запустить тестовый экземпляр (по умолчанию — порт 2021) |
|
||||
| `yarn twenty server stop --test` | Остановить его |
|
||||
| `yarn twenty server status --test` | Показать его статус |
|
||||
| `yarn twenty server logs --test` | Транслировать его журналы |
|
||||
| `yarn twenty server reset --test` | Стереть его данные |
|
||||
| `yarn twenty server upgrade --test` | Обновить его образ |
|
||||
|
||||
The test instance has its own container (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`), and config — it runs alongside your main instance without conflicts. Combine `--test` with `--port` to override 2021.
|
||||
Тестовый экземпляр запускается в собственном контейнере Docker (`twenty-app-dev-test`) с выделенными томами (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) и собственной конфигурацией, поэтому он может работать параллельно с вашим основным экземпляром без конфликтов. Совместите `--test` с `--port`, чтобы переопределить значение по умолчанию (2021).
|
||||
|
||||
## Manual setup (without the scaffolder)
|
||||
## Ручная настройка (без генератора)
|
||||
|
||||
Skip the scaffolder if you're adding the SDK to an existing project:
|
||||
Пропустите генератор каркаса, если вы добавляете SDK в существующий проект:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
```
|
||||
|
||||
Add the script to `package.json`:
|
||||
Добавьте скрипт в `package.json`:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -65,8 +65,8 @@ Add the script to `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
You can now run `yarn twenty dev`, `yarn twenty server start`, and the rest.
|
||||
Теперь вы можете запускать `yarn twenty dev`, `yarn twenty server start` и остальные команды.
|
||||
|
||||
<Note>
|
||||
Don't install `twenty-sdk` globally — pin it per project so each app uses its own version.
|
||||
Не устанавливайте `twenty-sdk` глобально — фиксируйте версию в каждом проекте, чтобы каждое приложение использовало свою собственную версию.
|
||||
</Note>
|
||||
|
||||
+12
-12
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: What's inside a scaffolded Twenty app — files, folders, and what each one does.
|
||||
title: Структура проекта
|
||||
description: Что находится внутри сгенерированного приложения Twenty — файлы, папки и назначение каждого из них.
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
A new app generated by `npx create-twenty-app` looks like this:
|
||||
Новое приложение, сгенерированное с помощью `npx create-twenty-app`, выглядит так:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
@@ -25,16 +25,16 @@ my-twenty-app/
|
||||
README.md, LLMS.md
|
||||
```
|
||||
|
||||
## Key files
|
||||
## Ключевые файлы
|
||||
|
||||
| File / Folder | Purpose |
|
||||
| ---------------------------------------- | -------------------------------------------------------------- |
|
||||
| `src/application-config.ts` | **Required.** The main configuration file for your app. |
|
||||
| `src/default-role.ts` | Default role controlling what your logic functions can access. |
|
||||
| `src/constants/universal-identifiers.ts` | Auto-generated UUIDs and metadata (display name, description). |
|
||||
| `src/__tests__/` | Integration tests (setup + example test). |
|
||||
| `public/` | Static assets (images, fonts) served with your app. |
|
||||
| Файл / Папка | Назначение |
|
||||
| ---------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `src/application-config.ts` | **Обязательно.** Основной файл конфигурации для вашего приложения. |
|
||||
| `src/default-role.ts` | Роль по умолчанию, контролирующая, к чему имеют доступ ваши логические функции. |
|
||||
| `src/constants/universal-identifiers.ts` | Автоматически генерируемые UUID и метаданные (отображаемое имя, описание). |
|
||||
| `src/__tests__/` | Интеграционные тесты (настройка + пример теста). |
|
||||
| `public/` | Статические ресурсы (изображения, шрифты), обслуживаемые вместе с вашим приложением. |
|
||||
|
||||
<Note>
|
||||
**File organization is up to you.** The folders above are conventions — the SDK detects entities via AST analysis on `export default defineEntity(...)` calls regardless of where the file lives.
|
||||
**Организация файлов — на ваше усмотрение.** Приведенные выше папки являются соглашениями — SDK обнаруживает сущности с помощью AST-анализа вызовов `export default defineEntity(...)` независимо от того, где расположен файл.
|
||||
</Note>
|
||||
|
||||
@@ -1,184 +1,184 @@
|
||||
---
|
||||
title: Quick Start
|
||||
title: Быстрый старт
|
||||
icon: rocket
|
||||
description: Create your first Twenty app in minutes.
|
||||
description: Создайте своё первое приложение Twenty за считанные минуты.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
## Требования
|
||||
|
||||
* **Node.js 24+** — [Download](https://nodejs.org/)
|
||||
* **Yarn 4** — bundled with Node via Corepack. Enable it: `corepack enable`
|
||||
* **Docker** — [Download](https://www.docker.com/products/docker-desktop/). Needed to run a local Twenty server. Skip if you already have Twenty running elsewhere.
|
||||
* **Node.js 24+** — [Скачать здесь](https://nodejs.org/)
|
||||
* **Yarn 4** — поставляется вместе с Node.js через Corepack. Включите его, выполнив `corepack enable`
|
||||
* **Docker** — [Скачать здесь](https://www.docker.com/products/docker-desktop/). Требуется для запуска локального экземпляра Twenty. Пропустите, если у вас уже запущен Twenty в другом месте.
|
||||
|
||||
Building a Twenty app has three phases. The scaffolder collapses them into one happy-path command, but each phase is a separate concept — when something fails, knowing which phase you're in tells you what to fix.
|
||||
Создание приложения Twenty включает три фазы. Генератор каркаса объединяет их в одну команду для идеального сценария (happy path), но каждая фаза — отдельная концепция: когда что-то идёт не так, понимание того, на какой фазе вы находитесь, подскажет, что исправить.
|
||||
|
||||
| Phase | What you do | Tool | Result |
|
||||
| ------------------- | ---------------------------------- | ----------------------------- | ----------------------------- |
|
||||
| **1. Scaffold** | Generate the app's source code | `npx create-twenty-app` | A TypeScript project on disk |
|
||||
| **2. Run a server** | Start a Twenty server to sync into | Docker + `yarn twenty server` | A running Twenty instance |
|
||||
| **3. Sync** | Live-sync your code to the server | `yarn twenty dev` | Your changes appear in the UI |
|
||||
| Фаза | Что вы делаете | Инструмент | Результат |
|
||||
| ----------------------- | ------------------------------------------------- | ----------------------------- | -------------------------------------- |
|
||||
| **1. Создание каркаса** | Сгенерировать исходный код приложения | `npx create-twenty-app` | Проект TypeScript на диске |
|
||||
| **2. Запустить сервер** | Запустить сервер Twenty для синхронизации | Docker + `yarn twenty server` | Запущенный экземпляр Twenty |
|
||||
| **3. Синхронизация** | Синхронизируйте код с сервером в реальном времени | `yarn twenty dev` | Ваши изменения появляются в интерфейсе |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Scaffold your project
|
||||
## Фаза 1 — Сгенерируйте каркас проекта
|
||||
|
||||
Create a new app from the template:
|
||||
Создайте новое приложение из шаблона:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
You'll be prompted for a name and description — press **Enter** for the defaults. This generates a TypeScript project in `my-twenty-app/` with a starter `application-config.ts`, a default role, a CI workflow, and an integration test.
|
||||
Вам будет предложено ввести имя и описание — нажмите **Enter**, чтобы принять значения по умолчанию. Это создаст проект TypeScript в `my-twenty-app/` с начальным файлом `application-config.ts`, ролью по умолчанию, рабочим процессом CI и интеграционным тестом.
|
||||
|
||||
**After this phase:** you have an app's source code on your machine. It isn't running yet — that's Phase 2.
|
||||
**После этой фазы:** у вас есть исходный код приложения на вашем компьютере. Он ещё не запущен — это фаза 2.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Run a local Twenty server
|
||||
## Фаза 2 — Запустите локальный сервер Twenty
|
||||
|
||||
Your app needs a Twenty server to sync into. The server is a full Twenty instance — UI, GraphQL API, PostgreSQL — running locally in Docker. Your local code uploads its definitions to that server, which makes them appear in the UI.
|
||||
Вашему приложению нужен сервер Twenty для синхронизации. Сервер — это полноценный экземпляр Twenty — UI, GraphQL API, PostgreSQL — работающий локально в Docker. Ваш локальный код загружает свои определения на этот сервер, благодаря чему они появляются в интерфейсе.
|
||||
|
||||
The scaffolder offers to start one for you:
|
||||
Генератор каркаса предложит запустить его за вас:
|
||||
|
||||
> **Would you like to set up a local Twenty instance?**
|
||||
> **Хотите настроить локальный экземпляр Twenty?**
|
||||
|
||||
* **Yes (recommended)** — pulls the `twentycrm/twenty-app-dev` Docker image and starts it on port `2020`. Make sure Docker is running first.
|
||||
* **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote add`.
|
||||
* **Да (рекомендуется)** — скачивает Docker-образ `twentycrm/twenty-app-dev` и запускает его на порту `2020`. Сначала убедитесь, что Docker запущен.
|
||||
* **Нет** — выберите это, если у вас уже есть сервер Twenty, к которому вы хотите подключиться. Позже вы можете подключить его с помощью `yarn twenty remote add`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Запустить локальный экземпляр?" />
|
||||
</div>
|
||||
|
||||
Once the server is up, a browser opens for sign-in. Use the pre-seeded demo account:
|
||||
Когда сервер будет запущен, откроется браузер для входа. Используйте предварительно созданную демонстрационную учётную запись:
|
||||
|
||||
* **Email:** `tim@apple.dev`
|
||||
* **Password:** `tim@apple.dev`
|
||||
* **Электронная почта:** `tim@apple.dev`
|
||||
* **Пароль:** `tim@apple.dev`
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty login screen" />
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Экран входа в Twenty" />
|
||||
</div>
|
||||
|
||||
Click **Authorize** on the next screen — this gives the CLI access to your workspace.
|
||||
На следующем экране нажмите **Authorize** — это даст CLI доступ к вашему рабочему пространству.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty CLI authorization screen" />
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Экран авторизации Twenty CLI" />
|
||||
</div>
|
||||
|
||||
Your terminal will confirm everything is set up.
|
||||
В вашем терминале появится подтверждение, что всё настроено.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="App scaffolded successfully" />
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="Каркас приложения успешно создан" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a running Twenty server at [http://localhost:2020](http://localhost:2020) with your CLI authorized to sync to it.
|
||||
**После этой фазы:** у вас запущен сервер Twenty на [http://localhost:2020](http://localhost:2020), а ваш CLI авторизован для синхронизации с ним.
|
||||
|
||||
<Note>
|
||||
If Docker isn't installed or running, the scaffolder will tell you the right start command for your OS. Once Docker is up, you can resume with `yarn twenty server start` — no need to re-scaffold.
|
||||
Если Docker не установлен или не запущен, генератор каркаса подскажет правильную команду запуска для вашей ОС. Когда Docker будет запущен, вы можете продолжить с `yarn twenty server start` — заново генерировать каркас не нужно.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Sync your changes
|
||||
## Фаза 3 — Синхронизируйте свои изменения
|
||||
|
||||
This is the inner loop you'll spend most of your time in.
|
||||
Это внутренний цикл, в котором вы проведёте большую часть времени.
|
||||
|
||||
```bash filename="Terminal"
|
||||
cd my-twenty-app
|
||||
yarn twenty dev
|
||||
```
|
||||
|
||||
This watches `src/`, rebuilds on every change, and syncs the result to the server. Edit a file, save, and within a second the server reflects the change. You'll see a live status panel in your terminal.
|
||||
Эта команда отслеживает `src/`, пересобирает при каждом изменении и синхронизирует результат с сервером. Отредактируйте файл, сохраните — и через секунду сервер отразит изменения. В терминале появится панель текущего статуса.
|
||||
|
||||
For more detailed output (build logs, sync requests, error traces), add `--verbose`.
|
||||
Для более подробного вывода (журналы сборки, запросы синхронизации, трассировки ошибок) добавьте `--verbose`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Dev mode terminal output" />
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="Вывод терминала в режиме разработки" />
|
||||
</div>
|
||||
|
||||
Open [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). You should see your app under **Your Apps**.
|
||||
Откройте [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Вы должны увидеть своё приложение в разделе **Your Apps**.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Your Apps list showing My twenty app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Список Your Apps с приложением My twenty app" />
|
||||
</div>
|
||||
|
||||
Click **My twenty app** to see its **application registration** — a server-level record describing your app (name, identifier, OAuth credentials, source). One registration can be installed across multiple workspaces on the same server.
|
||||
Нажмите **My twenty app**, чтобы открыть его регистрацию приложения — запись на уровне сервера, описывающую ваше приложение (имя, идентификатор, учётные данные OAuth, источник). Одну и ту же регистрацию можно установить в нескольких рабочих пространствах на одном сервере.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Application registration details" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="Сведения о регистрации приложения" />
|
||||
</div>
|
||||
|
||||
Click **View installed app** to see the workspace install. The **About** tab shows version and management options.
|
||||
Нажмите **View installed app**, чтобы посмотреть установку в рабочем пространстве. Вкладка **About** показывает версию и параметры управления.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Installed app" />
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="Установленное приложение" />
|
||||
</div>
|
||||
|
||||
**After this phase:** you have a live development loop. Edit any file in `src/` and it appears in the UI.
|
||||
**После этой фазы:** у вас есть интерактивный цикл разработки. Отредактируйте любой файл в `src/`, и он появится в интерфейсе.
|
||||
|
||||
### One-shot sync for CI and scripts
|
||||
### Разовая синхронизация для CI и скриптов
|
||||
|
||||
Pass `--once` to run a single build + sync and exit — same pipeline, no watcher:
|
||||
Передайте `--once`, чтобы выполнить одну сборку и синхронизацию и завершить работу — тот же конвейер, без наблюдателя:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once
|
||||
```
|
||||
|
||||
| Command | Behavior | When to use |
|
||||
| ------------------------ | ------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `yarn twenty dev` | Watches and re-syncs on every change. Runs until you stop it. | Interactive local development. |
|
||||
| `yarn twenty dev --once` | Single build + sync, exits `0` on success, `1` on failure. | CI, pre-commit hooks, AI agents, scripted workflows. |
|
||||
| Команда | Поведение | Когда использовать |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `yarn twenty dev` | Отслеживает и повторно синхронизирует при каждом изменении. Продолжает работать, пока вы его не остановите. | Интерактивная локальная разработка. |
|
||||
| `yarn twenty dev --once` | Одна сборка и синхронизация, завершает работу с кодом `0` при успехе и `1` при ошибке. | CI, хуки pre-commit, AI-агенты, скриптовые рабочие процессы. |
|
||||
|
||||
Both modes need a server in development mode and an authenticated remote.
|
||||
Оба режима требуют сервер в режиме разработки и аутентифицированный удалённый сервер.
|
||||
|
||||
<Warning>
|
||||
Dev mode is only available on Twenty instances running in development (`NODE_ENV=development`). Production instances reject dev sync requests — use `yarn twenty deploy` to deploy to production servers. See [Publishing](/l/ru/developers/extend/apps/operations/publishing).
|
||||
Режим разработки доступен только на экземплярах Twenty, запущенных в режиме разработки (`NODE_ENV=development`). Экземпляры в продакшене отклоняют запросы синхронизации из режима разработки — используйте `yarn twenty deploy` для развёртывания на производственные серверы. См. [Публикация](/l/ru/developers/extend/apps/operations/publishing).
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Starting from an example
|
||||
## Начните с примера
|
||||
|
||||
Use `--example` to start with a more complete project (custom objects, fields, logic functions, front components):
|
||||
Используйте `--example`, чтобы начать с более полного проекта (пользовательские объекты, поля, логические функции, фронтенд-компоненты):
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app --example postcard
|
||||
```
|
||||
|
||||
Examples live in [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). You can also scaffold individual entities into an existing project with `yarn twenty add` — see [Scaffolding](/l/ru/developers/extend/apps/getting-started/scaffolding).
|
||||
Примеры берутся из каталога [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) на GitHub. Вы также можете сгенерировать каркас отдельных сущностей в существующем проекте с помощью `yarn twenty add` — см. [Scaffolding](/l/ru/developers/extend/apps/getting-started/scaffolding).
|
||||
|
||||
---
|
||||
|
||||
## What you can build
|
||||
## Что вы можете создать
|
||||
|
||||
Apps are composed of **entities** — each defined as a TypeScript file with a single `export default`:
|
||||
Приложения состоят из **сущностей** — каждая определена как файл TypeScript с одним `export default`:
|
||||
|
||||
| Entity | What it does |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------- |
|
||||
| **Objects & Fields** | Custom data models (Post Card, Invoice, etc.) with typed fields |
|
||||
| **Logic functions** | Server-side TypeScript triggered by HTTP routes, cron schedules, or database events |
|
||||
| **Front components** | React components that render inside Twenty's UI (side panel, widgets, command menu) |
|
||||
| **Skills & Agents** | AI capabilities — reusable instructions and autonomous assistants |
|
||||
| **Views & Navigation** | Pre-configured list views and sidebar menu items |
|
||||
| **Page layouts** | Custom record detail pages with tabs and widgets |
|
||||
| Сущность | Что делает |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| **Объекты и поля** | Пользовательские модели данных (почтовая открытка, счёт и т. д.) с типизированными полями |
|
||||
| **Логические функции** | Серверный TypeScript, запускаемый HTTP-маршрутами, расписаниями cron или событиями базы данных |
|
||||
| **Фронтенд-компоненты** | React-компоненты, которые отображаются внутри интерфейса Twenty (боковая панель, виджеты, командное меню) |
|
||||
| **Навыки и агенты** | Возможности ИИ — многократно используемые инструкции и автономные помощники |
|
||||
| **Представления и навигация** | Предварительно настроенные представления списков и элементы бокового меню |
|
||||
| **Макеты страниц** | Пользовательские страницы сведений о записи с вкладками и виджетами |
|
||||
|
||||
Full reference: [Concepts](/l/ru/developers/extend/apps/getting-started/concepts).
|
||||
Полная справка: [Concepts](/l/ru/developers/extend/apps/getting-started/concepts).
|
||||
|
||||
## Next steps
|
||||
## Следующие шаги
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Config" icon="screwdriver-wrench" href="/l/ru/developers/extend/apps/config/overview">
|
||||
Application identity, default role, install hooks, public assets.
|
||||
<Card title="Конфигурация" icon="screwdriver-wrench" href="/l/ru/developers/extend/apps/config/overview">
|
||||
Идентификация приложения, роль по умолчанию, хуки установки, публичные ассеты.
|
||||
</Card>
|
||||
<Card title="Data" icon="database" href="/l/ru/developers/extend/apps/data/overview">
|
||||
Objects, fields, and bidirectional relations.
|
||||
<Card title="Данные" icon="database" href="/l/ru/developers/extend/apps/data/overview">
|
||||
Объекты, поля и двунаправленные связи.
|
||||
</Card>
|
||||
<Card title="Logic" icon="bolt" href="/l/ru/developers/extend/apps/logic/overview">
|
||||
Logic functions, skills, agents, and OAuth connections.
|
||||
<Card title="Логика" icon="bolt" href="/l/ru/developers/extend/apps/logic/overview">
|
||||
Логические функции, скиллы, агенты и OAuth-подключения.
|
||||
</Card>
|
||||
<Card title="Layout" icon="table-columns" href="/l/ru/developers/extend/apps/layout/overview">
|
||||
Views, navigation, page layouts, front components.
|
||||
<Card title="Макет" icon="table-columns" href="/l/ru/developers/extend/apps/layout/overview">
|
||||
Представления, навигация, макеты страниц, фронтовые компоненты.
|
||||
</Card>
|
||||
<Card title="Operations" icon="rocket" href="/l/ru/developers/extend/apps/operations/overview">
|
||||
CLI, testing, remotes, CI, and publishing your app.
|
||||
<Card title="Операции" icon="rocket" href="/l/ru/developers/extend/apps/operations/overview">
|
||||
CLI, тестирование, ремоуты, CI и публикация вашего приложения.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user