twenty-sdk stellt typisierte Bausteine zum Erstellen Ihrer App bereit. Diese Seite behandelt alle im SDK verfügbaren Entitätstypen und API-Clients.
DefineEntity-Funktionen
Das SDK stellt Funktionen bereit, um die Entitäten Ihrer App zu definieren. Sie müssenexport default defineEntity({...}) verwenden, damit das SDK Ihre Entitäten erkennt. Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit.
export default defineEntity(...), unabhängig davon, wo sich die Datei befindet. Das Gruppieren von Dateien nach Typ (z. B. logic-functions/, roles/) ist lediglich eine Konvention, keine Voraussetzung.Rolle definieren
Rollenberechtigungen und Objektzugriff konfigurieren
Rolle definieren
Rollenberechtigungen und Objektzugriff konfigurieren
Anwendung definieren
Anwendungsmetadaten konfigurieren (erforderlich, eine pro App)
Anwendung definieren
Anwendungsmetadaten konfigurieren (erforderlich, eine pro App)
defineApplication haben, der Folgendes beschreibt:- Identität: Bezeichner, Anzeigename und Beschreibung.
- Berechtigungen: welche Rolle ihre Funktionen und Frontend-Komponenten verwenden.
- (Optional) Variablen: Schlüssel–Wert-Paare, die Ihren Funktionen als Umgebungsvariablen zur Verfügung gestellt werden.
- (Optional) Pre-/Post-Installationsfunktionen: Logikfunktionen, die vor oder nach der Installation ausgeführt werden.
universalIdentifier-Felder sind deterministische IDs, die Ihnen gehören. Erzeugen Sie sie einmal und halten Sie sie über Synchronisierungen hinweg stabil.applicationVariableswerden zu Umgebungsvariablen für Ihre Funktionen und Frontend-Komponenten (z. B. istDEFAULT_RECIPIENT_NAMEalsprocess.env.DEFAULT_RECIPIENT_NAMEverfügbar).defaultRoleUniversalIdentifiermuss auf eine mitdefineRole()definierte Rolle verweisen (siehe oben).- Pre- und Post-Installationsfunktionen werden während des Manifest-Builds automatisch erkannt — Sie müssen sie in
defineApplication()nicht referenzieren.
Marktplatz-Metadaten
Wenn Sie planen, Ihre App zu veröffentlichen, steuern diese optionalen Felder, wie Ihre App im Marktplatz erscheint:| 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 Marketplace 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 |
Rollen und Berechtigungen
Das FelddefaultRoleUniversalIdentifier in application-config.ts legt die Standardrolle fest, die von den Logikfunktionen und Frontend-Komponenten Ihrer App verwendet wird. Details finden Sie oben unter defineRole.- Das zur Laufzeit als
TWENTY_APP_ACCESS_TOKENinjizierte Token wird aus dieser Rolle abgeleitet. - Der typisierte Client ist auf die dieser Rolle gewährten Berechtigungen beschränkt.
- Befolgen Sie das Least-Privilege-Prinzip: Erstellen Sie eine dedizierte Rolle nur mit den Berechtigungen, die Ihre Funktionen benötigen.
Standard-Funktionsrolle
Wenn Sie eine neue App erzeugen, erstellt die CLI eine Standard-Rolldatei:universalIdentifier dieser Rolle wird in application-config.ts als defaultRoleUniversalIdentifier referenziert:- *.role.ts definiert, was die Rolle darf.
- application-config.ts verweist auf diese Rolle, sodass Ihre Funktionen deren Berechtigungen erben.
- Beginnen Sie mit der vorab erstellten Rolle und schränken Sie sie schrittweise gemäß dem Least-Privilege-Prinzip ein.
- Ersetzen Sie
objectPermissionsundfieldPermissionsdurch die Objekte und Felder, die Ihre Funktionen tatsächlich benötigen. permissionFlagssteuern den Zugriff auf Funktionen auf Plattformebene. Halten Sie sie minimal.- Ein funktionierendes Beispiel finden Sie unter:
hello-world/src/roles/function-role.ts.
Objekt definieren
Benutzerdefinierte Objekte mit Feldern definieren
Objekt definieren
Benutzerdefinierte Objekte mit Feldern definieren
defineObject(), um Objekte mit eingebauter Validierung zu definieren:- Verwenden Sie
defineObject()für eingebaute Validierung und bessere IDE-Unterstützung. - Der
universalIdentifiermuss eindeutig und über Deployments hinweg stabil sein. - Jedes Feld benötigt
name,type,labelund einen eigenen stabilenuniversalIdentifier. - Das Array
fieldsist optional — Sie können Objekte ohne benutzerdefinierte Felder definieren. - Sie können mit
yarn twenty addneue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen.
id, name, createdAt, updatedAt, createdBy, updatedBy und deletedAt.
Sie müssen diese nicht in Ihrem fields-Array definieren — fügen Sie nur Ihre benutzerdefinierten Felder hinzu.
Sie können Standardfelder überschreiben, indem Sie in Ihrem fields-Array ein Feld mit demselben Namen definieren,
dies wird jedoch nicht empfohlen.defineField — Standardfelder
Bestehende Objekte mit zusätzlichen Feldern erweitern
defineField — Standardfelder
Bestehende Objekte mit zusätzlichen Feldern erweitern
defineField(), um Objekten, die Ihnen nicht gehören — etwa Standardobjekten von Twenty (Person, Company usw.) — Felder hinzuzufügen oder Objekten aus anderen Apps. Im Gegensatz zu Inline-Feldern in defineObject() benötigen eigenständige Felder einen objectUniversalIdentifier, um anzugeben, welches Objekt sie erweitern:- Der
objectUniversalIdentifieridentifiziert das Zielobjekt. Für Standardobjekte verwenden SieSTANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, die austwenty-sdkexportiert werden. - Wenn Sie Felder inline in
defineObject()definieren, benötigen SieobjectUniversalIdentifiernicht — er wird vom übergeordneten Objekt geerbt. defineField()ist die einzige Möglichkeit, Felder zu Objekten hinzuzufügen, die Sie nicht mitdefineObject()erstellt haben.
defineField — Relationsfelder
Objekte mit bidirektionalen Relationen verbinden
defineField — Relationsfelder
Objekte mit bidirektionalen Relationen verbinden
| 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 | Nein (inverse Seite) |
Wie Relationen funktionieren
Jede Relation erfordert zwei Felder, die sich gegenseitig referenzieren:- Die MANY_TO_ONE-Seite — befindet sich auf dem Objekt, das den Fremdschlüssel hält
- Die ONE_TO_MANY-Seite — befindet sich auf dem Objekt, dem die Sammlung gehört
FieldType.RELATION und verweisen über relationTargetFieldMetadataUniversalIdentifier gegenseitig aufeinander.Beispiel: Postkarte hat viele Empfänger
Angenommen, einePostCard kann an viele PostCardRecipient-Datensätze gesendet werden. Jeder Empfänger gehört genau zu einer Postkarte.Schritt 1: Definieren Sie die ONE_TO_MANY-Seite auf PostCard (die “eine” Seite):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 Datei. Das Build-System löst dies zur Kompilierzeit auf.Relationen zu Standardobjekten
Um eine Relation mit einem integrierten Twenty-Objekt (Person, Company usw.) zu erstellen, verwenden SieSTANDARD_OBJECT_UNIVERSAL_IDENTIFIERS:Eigenschaften von Relationsfeldern
| 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-Relationsfelder in defineObject
Sie können Relationsfelder auch direkt innerhalb vondefineObject() definieren. In diesem Fall lassen Sie objectUniversalIdentifier weg — er wird vom übergeordneten Objekt geerbt:defineLogicFunction
Logikfunktionen und deren Trigger definieren
defineLogicFunction
Logikfunktionen und deren Trigger definieren
defineLogicFunction(), um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren.- httpRoute: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode unter dem Endpunkt
/s/bereit:
z. B.path: '/post-card/create'ist unterhttps://your-twenty-server.com/s/post-card/createaufrufbar
- cron: Führt Ihre Funktion nach Zeitplan mithilfe eines CRON-Ausdrucks aus.
- databaseEvent: Wird bei Lebenszyklusereignissen von Workspace-Objekten ausgeführt. Wenn die Ereignisoperation
updatedist, können bestimmte zu überwachende Felder im ArrayupdatedFieldsangegeben werden. Wenn das Array undefiniert oder leer ist, löst jede Aktualisierung die Funktion aus.
z. B.person.updated,*.created,company.*
Routen-Trigger-Payload
Wenn ein Route-Trigger Ihre Logikfunktion aufruft, erhält sie einRoutePayload-Objekt, das dem AWS-HTTP-API-v2-Format folgt.
Importieren Sie den Typ RoutePayload aus twenty-sdk:RoutePayload hat die folgende Struktur:| 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 } |
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
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 ArrayforwardedRequestHeaders auf:event.headers['content-type']).Eine Funktion als Tool bereitstellen
Logikfunktionen können als Tools für KI-Agenten und Workflows verfügbar gemacht werden. Wenn eine Funktion als Tool markiert ist, wird sie von den KI-Funktionen von Twenty auffindbar und kann in Workflow-Automatisierungen verwendet werden.Um eine Logikfunktion als Tool zu markieren, setzen SieisTool: true:- Sie können
isToolmit Triggern kombinieren — eine Funktion kann gleichzeitig sowohl ein Tool (von KI-Agenten aufrufbar) als auch durch Ereignisse ausgelöst werden. toolInputSchema(optional): Ein JSON-Schema-Objekt, das die Parameter beschreibt, die Ihre Funktion akzeptiert. Das Schema wird automatisch durch statische Analyse des Quellcodes ermittelt, Sie können es jedoch auch explizit festlegen:
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.definePreInstallLogicFunction
Eine Pre-Installations-Logikfunktion definieren (eine pro App)
definePreInstallLogicFunction
Eine Pre-Installations-Logikfunktion definieren (eine pro App)
- Pre-Installationsfunktionen verwenden
definePreInstallLogicFunction()— eine spezialisierte Variante, die Trigger-Einstellungen (cronTriggerSettings,databaseEventTriggerSettings,httpRouteTriggerSettings,isTool) weglässt. - Der Handler erhält ein
InstallLogicFunctionPayloadmit{ previousVersion: string }— die Version der App, die zuvor installiert war (oder eine leere Zeichenkette bei Neuinstallationen). - Pro Anwendung ist nur eine Pre-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
- Der
universalIdentifierder Funktion wird während des Builds im Anwendungsmanifest automatisch alspreInstallLogicFunctionUniversalIdentifiergesetzt — Sie müssen ihn nicht indefineApplication()referenzieren. - Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Vorbereitungsvorgänge zu ermöglichen.
definePostInstallLogicFunction
Eine Post-Installations-Logikfunktion definieren (eine pro App)
definePostInstallLogicFunction
Eine Post-Installations-Logikfunktion definieren (eine pro App)
- Post-Installationsfunktionen verwenden
definePostInstallLogicFunction()— eine spezialisierte Variante, die Trigger-Einstellungen (cronTriggerSettings,databaseEventTriggerSettings,httpRouteTriggerSettings,isTool) weglässt. - Der Handler erhält ein
InstallLogicFunctionPayloadmit{ previousVersion: string }— die Version der App, die zuvor installiert war (oder eine leere Zeichenkette bei Neuinstallationen). - Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
- Der
universalIdentifierder Funktion wird während des Builds im Anwendungsmanifest automatisch alspostInstallLogicFunctionUniversalIdentifiergesetzt — Sie müssen ihn nicht indefineApplication()referenzieren. - Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen.
defineFrontComponent
Frontend-Komponenten für benutzerdefinierte UI definieren
defineFrontComponent
Frontend-Komponenten für benutzerdefinierte UI definieren
Wo Front-Komponenten verwendet werden können
Front-Komponenten können an zwei Stellen innerhalb von Twenty gerendert werden:- 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 eingebettet werden. Beim Konfigurieren eines Dashboards oder eines Datensatzseiten-Layouts können Benutzer ein Front-Komponenten-Widget hinzufügen.
Einfaches Beispiel
Der schnellste Weg, eine Frontend-Komponente in Aktion zu sehen, ist, sie als Befehl zu registrieren. Das Hinzufügen einescommand-Felds mit isPinned: true lässt sie als Schnellaktionsschaltfläche oben rechts auf der Seite erscheinen — kein Seitenlayout erforderlich:yarn twenty dev erscheint die Schnellaktion oben rechts auf der Seite:
Konfigurationsfelder
| 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) |
command | Nein | Die Komponente als Befehl registrieren (siehe unten Befehlsoptionen) |
Eine Frontend-Komponente auf einer Seite platzieren
Über Befehle hinaus können Sie eine Frontend-Komponente direkt in eine Datensatzseite einbetten, indem Sie sie als Widget in einem Seitenlayout hinzufügen. Details finden Sie im Abschnitt definePageLayout.Headless vs. Nicht-Headless
Front-Komponenten gibt es in zwei Rendering-Modi, die durch die OptionisHeadless gesteuert werden: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) — 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.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-Komponenten
Das Pakettwenty-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.Importieren Sie sie aus twenty-sdk/command:Command— Führt einen asynchronen Callback über das Propexecuteaus.CommandLink— Navigiert zu einem App-Pfad. Props:to,params,queryParams,options.CommandModal— Öffnet einen Bestätigungsdialog. Bestätigt der Benutzer, wird der Callbackexecuteausgeführt. Props:title,subtitle,execute,confirmButtonText,confirmButtonAccent.CommandOpenSidePanelPage— Öffnet eine bestimmte Seite im Seitenpanel. Props:page,pageTitle,pageIcon.
Command verwendet, um eine Aktion aus dem Befehlsmenü auszuführen:CommandModal verwendet, um vor der Ausführung um Bestätigung zu bitten:Zugriff auf den Laufzeitkontext
Verwenden Sie innerhalb Ihrer Komponente SDK-Hooks, um auf den aktuellen Benutzer, den Datensatz und die Komponenteninstanz zuzugreifen:| Hook | Gibt zurück | Beschreibung |
|---|---|---|
useUserId() | string oder null | Die ID des aktuellen Benutzers |
useRecordId() | string oder null | Die ID des aktuellen Datensatzes (wenn auf einer Datensatzseite platziert) |
useFrontComponentId() | string | Die ID dieser Komponenteninstanz |
useFrontComponentExecutionContext(selector) | variiert | Zugriff auf den vollständigen Ausführungskontext mit einer Selektorfunktion |
Host-Kommunikations-API
Frontend-Komponenten können Navigation, Modals und Benachrichtigungen mittels Funktionen austwenty-sdk auslösen:| 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 |
Befehlsoptionen
Das Hinzufügen einescommand-Felds zu defineFrontComponent registriert die Komponente im Befehlsmenü (Cmd+K). Wenn isPinned true ist, erscheint sie außerdem als Schnellaktionsschaltfläche oben rechts auf der Seite.| Feld | Erforderlich | Beschreibung |
|---|---|---|
universalIdentifier | Ja | Stabile eindeutige ID für den Befehl |
label | Ja | Vollständiges Label, das im Befehlsmenü (Cmd+K) angezeigt wird |
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, um dynamisch zu steuern, ob der Befehl sichtbar ist (siehe unten) |
Bedingte Verfügbarkeitsausdrücke
Mit dem FeldconditionalAvailabilityExpression 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:| Variable | Typ | Beschreibung |
|---|---|---|
pageType | string | 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 |
| 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 |
Öffentliche Assets
Frontend-Komponenten können mitgetPublicAssetUrl auf Dateien aus dem public/-Verzeichnis der App zugreifen:Styling
Frontend-Komponenten unterstützen mehrere Styling-Ansätze. Sie können verwenden:- 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
defineSkill
Skills für KI-Agenten definieren
defineSkill
Skills für KI-Agenten definieren
defineSkill(), um Skills mit eingebauter Validierung zu definieren:nameist eine eindeutige Kennung (als Zeichenfolge) für den Skill (kebab-case empfohlen).labelist der menschenlesbare Anzeigename, der in der UI angezeigt wird.contententhä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.
defineAgent
KI-Agenten mit benutzerdefinierten Prompts definieren
defineAgent
KI-Agenten mit benutzerdefinierten Prompts definieren
defineAgent(), um Agenten mit einem benutzerdefinierten System-Prompt zu erstellen:nameist die eindeutige Kennzeichnungs-Zeichenfolge für den Agenten (kebab-case empfohlen).labelist der in der UI angezeigte Anzeigename.promptist 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.
defineView
Gespeicherte Views für Objekte definieren
defineView
Gespeicherte Views für Objekte definieren
defineView(), um vorkonfigurierte Ansichten mit Ihrer App auszuliefern:objectUniversalIdentifiergibt an, auf welches Objekt diese Ansicht angewendet wird.keybestimmt den Ansichtstyp (z. B.ViewKey.INDEXfür die Hauptlistenansicht).fieldssteuert, welche Spalten erscheinen und in welcher Reihenfolge. Jedes Feld referenziert einenfieldMetadataUniversalIdentifier.- Für erweiterte Konfigurationen können Sie außerdem
filters,filterGroups,groupsundfieldGroupsdefinieren. positionsteuert die Reihenfolge, wenn mehrere Ansichten für dasselbe Objekt existieren.
defineNavigationMenuItem
Seitenleisten-Navigationslinks definieren
defineNavigationMenuItem
Seitenleisten-Navigationslinks definieren
definePageLayout
Benutzerdefinierte Seitenlayouts für Datensatzansichten definieren
definePageLayout
Benutzerdefinierte Seitenlayouts für Datensatzansichten definieren
definePageLayout(), um benutzerdefinierte Layouts mit Ihrer App auszuliefern:typeist typischerweise'RECORD_PAGE', um die Detailansicht eines bestimmten Objekts anzupassen.objectUniversalIdentifiergibt an, auf welches Objekt dieses Layout angewendet wird.- Jeder
tabdefiniert einen Abschnitt der Seite mittitle,positionundlayoutMode(CANVASfür ein freies Layout). - Jedes
widgetinnerhalb eines Tabs kann eine Frontend-Komponente, eine Relationenliste oder andere eingebaute Widget-Typen rendern. positionauf Tabs steuert deren Reihenfolge. Verwenden Sie höhere Werte (z. B. 50), um benutzerdefinierte Tabs hinter den integrierten zu platzieren.
Öffentliche Assets (Ordner public/)
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.
Für Dateien im Verzeichnis public/ gilt:
- Ö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
logoUrlundscreenshotsindefineApplication()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 buildbündelt alle öffentlichen Assets in der Distributionsausgabe.
Zugriff auf öffentliche Assets mit getPublicAssetUrl
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 einer Logikfunktion:
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.
Verwendung von npm-Paketen
Sie können in Ihrer App beliebige npm-Pakete installieren und verwenden. Sowohl Logikfunktionen als auch Frontend-Komponenten werden mit esbuild gebündelt, das alle Abhängigkeiten in die Ausgabe einbettet — zur Laufzeit sind keinenode_modules erforderlich.
Ein Paket installieren
Wie das Bundling funktioniert
Der Build-Schritt (yarn twenty dev oder yarn twenty build) 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.
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.
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.
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.
Entitäten mit yarn twenty add erstellen
Anstatt Entitätsdateien manuell zu erstellen, können Sie den interaktiven Scaffolder verwenden:
universalIdentifier und dem korrekten defineEntity()-Aufruf.
Sie können den Entitätstyp auch direkt übergeben, um die erste Eingabeaufforderung zu überspringen:
Verfügbare Entitätstypen
| 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 |
Was der Scaffolder generiert
Jeder Entitätstyp hat seine eigene Vorlage. Zum Beispiel fragtyarn twenty add object nach:
- Name (Singular) — z. B.
invoice - Name (Plural) — z. B.
invoices - Label (Singular) — automatisch aus dem Namen befüllt (z. B.
Invoice) - Label (Plural) — automatisch befüllt (z. B.
Invoices) - 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.
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.
Benutzerdefinierter Ausgabepfad
Verwenden Sie den Schalter--path, um die generierte Datei an einem benutzerdefinierten Ort abzulegen:
Typisierte API-Clients (twenty-client-sdk)
Das Pakettwenty-client-sdk stellt zwei typisierte GraphQL-Clients bereit, um aus Ihren Logikfunktionen und Frontend-Komponenten mit der Twenty-API zu interagieren.
| 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 |
CoreApiClient
Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern
CoreApiClient
Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern
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.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.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.Verwendung von CoreSchema für Typannotationen
CoreSchema stellt TypeScript-Typen bereit, die Ihren Arbeitsbereichsobjekten entsprechen — nützlich zum Typisieren von Komponentenzustand oder Funktionsparametern:MetadataApiClient
Konfiguration des Arbeitsbereichs, Anwendungen und Dateiuploads
MetadataApiClient
Konfiguration des Arbeitsbereichs, Anwendungen und Dateiuploads
MetadataApiClient ist im SDK bereits vorgefertigt enthalten (keine Generierung erforderlich). Er fragt den Endpunkt /metadata nach Arbeitsbereichskonfiguration, Anwendungen und Datei-Uploads ab.Dateien hochladen
DerMetadataApiClient enthält eine Methode uploadFile, um Dateien an Felder des Typs Datei anzuhängen:| 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 |
- Sie verwendet den
universalIdentifierdes Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist. - Die zurückgegebene
urlist eine signierte URL, mit der Sie auf die hochgeladene Datei zugreifen können.
TWENTY_API_URL— Basis-URL der Twenty-APITWENTY_APP_ACCESS_TOKEN— Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist
process.env. Die Berechtigungen des API-Schlüssels werden durch die Rolle bestimmt, auf die in defaultRoleUniversalIdentifier in Ihrer application-config.ts verwiesen wird.Ihre App testen
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 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.Einrichtung
Die erzeugte App enthält bereits Vitest. Wenn Sie es manuell einrichten, installieren Sie die Abhängigkeiten:vitest.config.ts im Stammverzeichnis Ihrer App:
Programmgesteuerte SDK-APIs
Der Subpfadtwenty-sdk/cli exportiert Funktionen, die Sie direkt aus Testcode aufrufen können:
| 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 |
success: boolean und entweder data oder error zurück.
Einen Integrationstest schreiben
Hier ist ein vollständiges Beispiel, das die App baut, bereitstellt und installiert und anschließend prüft, dass sie im Arbeitsbereich erscheint:Tests ausführen
Stellen Sie sicher, dass Ihr lokaler Twenty-Server läuft, und führen Sie dann Folgendes aus:Typprüfung
Sie können die Typprüfung Ihrer App auch ohne Tests ausführen:tsc --noEmit aus und meldet etwaige Typfehler.
CLI-Referenz
Zusätzlich zudev, build, add und typecheck bietet die CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen.
Funktionen ausführen (yarn twenty exec)
Eine Logikfunktion manuell ausführen, ohne sie über HTTP, Cron oder ein Datenbankereignis auszulösen:
Funktionsprotokolle ansehen (yarn twenty logs)
Ausführungsprotokolle für die Logikfunktionen Ihrer App streamen:
yarn twenty server logs, das die Docker-Container-Logs anzeigt. yarn twenty logs zeigt die Funktionsausführungsprotokolle Ihrer App vom Twenty-Server.Eine App deinstallieren (yarn twenty uninstall)
Entfernen Sie Ihre App aus dem aktiven Arbeitsbereich:
Remotes verwalten
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.~/.twenty/config.json gespeichert.
CI mit GitHub Actions
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.
Der Workflow:
- Checkt Ihren Code aus
- Startet einen temporären Twenty-Server mit der Aktion
twentyhq/twenty/.github/actions/spawn-twenty-docker-image - Installiert Abhängigkeiten mit
yarn install --immutable - Führt
yarn testaus, wobeiTWENTY_API_URLundTWENTY_API_KEYaus den Aktionsausgaben injiziert werden.
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.
Um eine bestimmte Twenty-Version statt latest festzulegen, ändern Sie die Umgebungsvariable TWENTY_VERSION oben im Workflow.