Skip to main content
The twenty-sdk package provides defineEntity functions to declare your app’s data model. Você deve usar export default defineEntity({...}) para que o SDK detecte suas entidades. Essas funções validam sua configuração em tempo de compilação e oferecem autocompletar na IDE e segurança de tipos.
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. Agrupar arquivos por tipo (por exemplo, logic-functions/, roles/) é apenas uma convenção, não um requisito.
Papéis encapsulam permissões sobre os objetos e ações do seu espaço de trabalho.
restricted-company-role.ts
Todo app deve ter exatamente uma chamada a defineApplication que descreve:
  • Identidade: identificadores, nome de exibição e descrição.
  • Permissões: qual papel é usado por suas funções e componentes de front-end.
  • Variáveis (opcional): pares chave–valor expostos às suas funções como variáveis de ambiente.
  • (Opcional) Funções de pré-instalação/pós-instalação: funções de lógica que são executadas antes ou depois da instalação.
src/application-config.ts
Notas:
  • 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() (veja acima).
  • 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().

Metadados do Marketplace

Se você planeja publicar seu app, estes campos opcionais controlam como seu app aparece no marketplace:

Papéis e permissões

O campo defaultRoleUniversalIdentifier em application-config.ts designa o papel padrão usado pelas funções de lógica e pelos componentes de front-end do seu app. Veja defineRole acima para detalhes.
  • O token em tempo de execução injetado como TWENTY_APP_ACCESS_TOKEN é derivado desse papel.
  • O cliente tipado é restrito às permissões concedidas a esse papel.
  • Siga o princípio do menor privilégio: crie um papel dedicado com apenas as permissões de que suas funções precisam.
Papel de função padrão
Ao criar um novo app com o scaffold, a CLI cria um arquivo de papel padrão:
src/roles/default-role.ts
O universalIdentifier desse papel é referenciado em application-config.ts como defaultRoleUniversalIdentifier:
  • *.role.ts define o que o papel pode fazer.
  • application-config.ts aponta para esse papel para que suas funções herdem suas permissões.
Notas:
  • Comece pelo papel gerado pelo scaffold e depois restrinja-o progressivamente seguindo o princípio do menor privilégio.
  • 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.
Objetos personalizados descrevem tanto o esquema quanto o comportamento de registros no seu espaço de trabalho. Use defineObject() para definir objetos com validação integrada:
postCard.object.ts
Pontos-chave:
  • Use defineObject() para validação integrada e melhor suporte na IDE.
  • 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.
  • Você pode criar novos objetos usando yarn twenty add, que orienta você sobre nomeação, campos e relacionamentos.
Os campos base são criados automaticamente. Quando você define um objeto personalizado, o Twenty adiciona automaticamente campos padrão como id, name, createdAt, updatedAt, createdBy, updatedBy e deletedAt. Você não precisa definir esses no seu array fields — adicione apenas seus campos personalizados. Você pode substituir os campos padrão definindo um campo com o mesmo nome no seu array fields, mas isso não é recomendado.
Use defineField() para adicionar campos a objetos que não são seus — como objetos padrão do Twenty (Person, Company, etc.). ou a objetos de outros apps. Ao contrário dos campos inline em defineObject(), os campos independentes exigem um objectUniversalIdentifier para especificar qual objeto eles estendem:
src/fields/company-loyalty-tier.field.ts
Pontos-chave:
  • objectUniversalIdentifier identifica o objeto de destino. Para objetos padrão, use STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS exportado de twenty-sdk.
  • Ao definir campos inline em defineObject(), você não precisa de objectUniversalIdentifier — ele é herdado do objeto pai.
  • defineField() é a única forma de adicionar campos a objetos que você não criou com defineObject().
As relações conectam objetos entre si. No Twenty, as relações são sempre bidirecionais — você define ambos os lados, e cada lado faz referência ao outro.Existem dois tipos de relação:

Como as relações funcionam

Toda relação requer dois campos que façam referência um ao outro:
  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
Ambos os campos usam FieldType.RELATION e fazem referência cruzada um ao outro via relationTargetFieldMetadataUniversalIdentifier.

Exemplo: Um cartão postal tem muitos destinatários

Suponha que um PostCard possa ser enviado para muitos registros PostCardRecipient. Cada destinatário pertence a exatamente um cartão postal.Etapa 1: Defina o lado ONE_TO_MANY em PostCard (o lado “um”):
src/fields/post-card-recipients-on-post-card.field.ts
Etapa 2: Defina o lado MANY_TO_ONE em PostCardRecipient (o lado “muitos” — contém a chave estrangeira):
src/fields/post-card-on-post-card-recipient.field.ts
Importações circulares: Ambos os campos de relação referenciam o 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 arquivo. O sistema de build resolve isso em tempo de compilação.

Relacionando a objetos padrão

Para criar uma relação com um objeto integrado do Twenty (Person, Company, etc.), use STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS:
src/fields/person-on-self-hosting-user.field.ts

Propriedades de campos de relação

Campos de relação inline em defineObject

Você também pode definir campos de relação diretamente dentro de defineObject(). Nesse caso, omita objectUniversalIdentifier — ele é herdado do objeto pai:

Gerando entidades com yarn twenty add

Em vez de criar arquivos de entidade manualmente, você pode usar o scaffolder interativo:
Isso solicita que você escolha um tipo de entidade e orienta você pelos campos obrigatórios. Ele gera um arquivo pronto para uso com um universalIdentifier estável e a chamada correta de defineEntity(). Você também pode passar o tipo de entidade diretamente para pular o primeiro prompt:

Tipos de entidade disponíveis

O que o scaffolder gera

Cada tipo de entidade tem seu próprio modelo. Por exemplo, yarn twenty add object solicita:
  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.
Outros tipos de entidade têm prompts mais simples — a maioria pede apenas um nome. 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.

Caminho de saída personalizado

Use a opção --path para colocar o arquivo gerado em um local personalizado: