i18n - docs translations (#20366)

Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-05-07 18:53:27 +02:00
committed by GitHub
parent 24e64350ee
commit 95bc8aea28
175 changed files with 5164 additions and 5007 deletions
@@ -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)* — keyvalue 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 chavevalor 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 asncrona 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 é **asncrona 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`.
Exampleseed a default `PostCard` record after install:
Exemplopopular 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ãopostal, 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>
@@ -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 themselvesfor 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 desmontampor 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 objetocolunas 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 layoutfor 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 existentepor 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 obtaineda **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 obtidasum **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 typingfor 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 ricapor 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 secretsthe `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 segredoa 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.