Skip to main content
The twenty-sdk package provides defineEntity functions to declare your app’s data model. Devi usare export default defineEntity({...}) affinché l’SDK rilevi le tue entità. Queste funzioni convalidano la configurazione in fase di build e offrono il completamento automatico nell’IDE e la sicurezza dei tipi.
L’organizzazione dei file dipende da te. Il rilevamento delle entità è basato sull’AST — l’SDK trova le chiamate a export default defineEntity(...) indipendentemente da dove si trova il file. Raggruppare i file per tipo (ad es., logic-functions/, roles/) è solo una convenzione, non un requisito.
I ruoli incapsulano i permessi sugli oggetti e sulle azioni del tuo spazio di lavoro.
restricted-company-role.ts
Ogni app deve avere esattamente una chiamata a defineApplication che descrive:
  • Identità: identificatori, nome visualizzato e descrizione.
  • Autorizzazioni: quale ruolo usano le sue funzioni e i componenti front-end.
  • Variabili (opzionali): coppie chiave–valore esposte alle funzioni come variabili d’ambiente.
  • (Opzionali) Funzioni di pre-installazione/post-installazione: funzioni logiche che vengono eseguite prima o dopo l’installazione.
src/application-config.ts
Note:
  • I campi universalIdentifier sono ID deterministici che possiedi. Generali una volta e mantienili stabili tra una sincronizzazione e l’altra.
  • applicationVariables diventano variabili d’ambiente per le tue funzioni e i componenti front-end (ad esempio, DEFAULT_RECIPIENT_NAME è disponibile come process.env.DEFAULT_RECIPIENT_NAME).
  • defaultRoleUniversalIdentifier deve fare riferimento a un ruolo definito con defineRole() (vedi sopra).
  • Le funzioni di pre-installazione e post-installazione vengono rilevate automaticamente durante il build del manifesto — non è necessario farvi riferimento in defineApplication().

Metadati del marketplace

Se prevedi di pubblicare la tua app, questi campi opzionali controllano come appare nel marketplace:

Ruoli e permessi

Il defaultRoleUniversalIdentifier in application-config.ts indica il ruolo predefinito utilizzato dalle funzioni logiche e dai componenti front-end della tua app. Vedi defineRole sopra per i dettagli.
  • Il token di runtime iniettato come TWENTY_APP_ACCESS_TOKEN è derivato da questo ruolo.
  • Il client tipizzato è limitato ai permessi concessi a quel ruolo.
  • Segui il principio del privilegio minimo: crea un ruolo dedicato con solo i permessi necessari alle tue funzioni.
Ruolo funzione predefinito
Quando esegui lo scaffolding di una nuova app, la CLI crea un file di ruolo predefinito:
src/roles/default-role.ts
L’universalIdentifier di questo ruolo viene referenziato in application-config.ts come defaultRoleUniversalIdentifier:
  • *.role.ts definisce ciò che il ruolo può fare.
  • application-config.ts punta a quel ruolo in modo che le tue funzioni ne ereditino i permessi.
Note:
  • Parti dal ruolo generato dallo scaffolder, quindi restringilo progressivamente seguendo il principio del privilegio minimo.
  • Sostituisci objectPermissions e fieldPermissions con gli oggetti e i campi di cui le tue funzioni hanno realmente bisogno.
  • permissionFlags controllano l’accesso alle funzionalità a livello di piattaforma. Mantienili al minimo.
  • Vedi un esempio funzionante: hello-world/src/roles/function-role.ts.
Gli oggetti personalizzati descrivono sia lo schema sia il comportamento per i record nel tuo spazio di lavoro. Usa defineObject() per definire oggetti con convalida integrata:
postCard.object.ts
Punti chiave:
  • Usa defineObject() per una convalida integrata e un migliore supporto IDE.
  • Il universalIdentifier deve essere univoco e stabile tra i deployment.
  • Ogni campo richiede un name, type, label e il proprio universalIdentifier stabile.
  • L’array fields è facoltativo: puoi definire oggetti senza campi personalizzati.
  • Puoi generare nuovi oggetti con yarn twenty add, che ti guida nella denominazione, nei campi e nelle relazioni.
I campi base vengono creati automaticamente. Quando definisci un oggetto personalizzato, Twenty aggiunge automaticamente i campi standard come id, name, createdAt, updatedAt, createdBy, updatedBy e deletedAt. Non è necessario definirli nel tuo array fields — aggiungi solo i tuoi campi personalizzati. Puoi sovrascrivere i campi predefiniti definendo un campo con lo stesso nome nel tuo array fields, ma non è consigliato.
Usa defineField() per aggiungere campi a oggetti che non possiedi — come gli oggetti standard di Twenty (Person, Company, ecc.) o oggetti di altre app. A differenza dei campi inline in defineObject(), i campi autonomi richiedono un objectUniversalIdentifier per specificare quale oggetto estendono:
src/fields/company-loyalty-tier.field.ts
Punti chiave:
  • objectUniversalIdentifier identifica l’oggetto di destinazione. Per gli oggetti standard, usa STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS esportati da twenty-sdk.
  • Quando definisci campi inline in defineObject(), non hai bisogno di objectUniversalIdentifier — viene ereditato dall’oggetto padre.
  • defineField() è l’unico modo per aggiungere campi a oggetti che non hai creato con defineObject().
Le relazioni collegano gli oggetti tra loro. In Twenty, le relazioni sono sempre bidirezionali — definisci entrambi i lati e ciascun lato fa riferimento all’altro.Esistono due tipi di relazione:

Come funzionano le relazioni

Ogni relazione richiede due campi che fanno riferimento l’uno all’altro:
  1. Il lato MANY_TO_ONE — risiede sull’oggetto che detiene la chiave esterna
  2. Il lato ONE_TO_MANY — risiede sull’oggetto che possiede la collezione
Entrambi i campi usano FieldType.RELATION e si riferiscono reciprocamente tramite relationTargetFieldMetadataUniversalIdentifier.

Esempio: Post Card ha molti destinatari

Supponiamo che un PostCard possa essere inviato a molti record PostCardRecipient. Ogni destinatario appartiene esattamente a una sola cartolina.Passaggio 1: definisci il lato ONE_TO_MANY su PostCard (il lato “uno”):
src/fields/post-card-recipients-on-post-card.field.ts
Passaggio 2: definisci il lato MANY_TO_ONE su PostCardRecipient (il lato “molti” — contiene la chiave esterna):
src/fields/post-card-on-post-card-recipient.field.ts
Importazioni circolari: Entrambi i campi di relazione fanno riferimento all’universalIdentifier dell’altro. Per evitare problemi di importazioni circolari, esporta gli ID dei campi come costanti denominate da ciascun file e importale nell’altro file. Il sistema di build le risolve in fase di compilazione.

Relazioni con gli oggetti standard

Per creare una relazione con un oggetto Twenty integrato (Person, Company, ecc.), usa STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS:
src/fields/person-on-self-hosting-user.field.ts

Proprietà dei campi di relazione

Campi di relazione inline in defineObject

Puoi anche definire i campi di relazione direttamente all’interno di defineObject(). In tal caso, ometti objectUniversalIdentifier — viene ereditato dall’oggetto padre:

Creazione di entità con lo scaffolding tramite yarn twenty add

Invece di creare manualmente i file delle entità, puoi usare lo scaffolder interattivo:
Questo ti chiede di scegliere un tipo di entità e ti guida attraverso i campi richiesti. Genera un file pronto all’uso con un universalIdentifier stabile e la corretta chiamata a defineEntity(). Puoi anche passare direttamente il tipo di entità per saltare il primo prompt:

Tipi di entità disponibili

Cosa genera lo scaffolder

Ogni tipo di entità ha il proprio template. Ad esempio, yarn twenty add object richiede:
  1. Nome (singolare) — ad es., invoice
  2. Nome (plurale) — ad es., invoices
  3. Etichetta (singolare) — compilata automaticamente dal nome (ad es., Invoice)
  4. Etichetta (plurale) — compilata automaticamente (ad es., Invoices)
  5. Creare una vista e una voce di navigazione? — se rispondi sì, lo scaffolder genera anche una vista corrispondente e un link nella barra laterale per il nuovo oggetto.
Gli altri tipi di entità hanno prompt più semplici — la maggior parte chiede solo un nome. Il tipo di entità field è più dettagliato: chiede il nome del campo, l’etichetta, il tipo (da un elenco di tutti i tipi di campo disponibili come TEXT, NUMBER, SELECT, RELATION, ecc.) e l’universalIdentifier dell’oggetto di destinazione.

Percorso di output personalizzato

Usa il flag --path per posizionare il file generato in una posizione personalizzata: