packages/twenty-codex-plugin/references/develop-app/app-structure.md
Use this reference when creating or modifying files inside an existing Twenty app.
Use ../manage-app/cli-and-sync.md for CLI command behavior, remotes, authentication, sync troubleshooting, build, deploy, logs, and CI/CD.
Before changing app entities, confirm the current directory is a Twenty app:
test -f package.json
test -f src/application-config.ts
If this fails, do not edit from the wrong folder. Check the current path and nearby app roots:
pwd
find . -maxdepth 3 -name package.json -o -path '*/src/application-config.ts'
Move to the matching app root before continuing. If no app exists, use create-app. If the folder exists but tooling, dependencies, remotes, authentication, sync, or builds are broken, use manage-app before changing app entities.
Inspect the app shape:
sed -n '1,220p' package.json
sed -n '1,220p' src/application-config.ts
find src -maxdepth 3 -type f | sort
find public -maxdepth 2 -type f | sort
Prefer the app CLI for new entities when interactive prompts are acceptable:
yarn twenty dev:add
For non-interactive agent work, direct file creation is often better. Use generated CLI templates, local SDK typings, or existing app files as the source of truth for imports and config shape.
Use official Twenty docs or local SDK source when exact imports, entity fields, or configuration shapes matter.
Add these alongside the scaffolded src/{fields,objects,logic-functions,front-components,page-layouts,navigation-menu-items,constants}/ as needed:
src/utils/ — pure helpers as <name>.util.ts, kept flat.src/types/ — one PascalCase type per file.src/<service>-client/ — wrappers around external SDKs or HTTP clients. One folder per service, matching Twenty's *-client convention (redis-client/, sdk-client/, ...).*.spec.ts in sibling __tests__/ folders next to the code they cover.Filenames are kebab-case; conventional suffixes are .util.ts, .dto.ts, .service.ts, .spec.ts — matching the Twenty backend.
Every helper, type, and client file exports exactly one thing. The rule counts exports, not declarations — there are no exceptions for utils/, types/, or <service>-client/.
src/types/<name>.ts while the util stays in src/utils/<name>.util.ts.// ❌ Bad — src/utils/parse-company.util.ts exports a type and a util
export type ParsedCompany = { id: string; name: string };
export const parseCompany = (raw: RawCompany): ParsedCompany => { /* ... */ };
// ✅ Good — src/utils/parse-company.util.ts (one export; the type is local)
type ParsedCompany = { id: string; name: string };
export const parseCompany = (raw: RawCompany): ParsedCompany => { /* ... */ };
// ✅ Good — when the type is shared, split it
// src/types/parsed-company.ts (one PascalCase type)
export type ParsedCompany = { id: string; name: string };
// src/utils/parse-company.util.ts (one util)
import { type ParsedCompany } from 'src/types/parsed-company';
export const parseCompany = (raw: RawCompany): ParsedCompany => { /* ... */ };
Entity files are consistent with this rule: front components, logic functions, and post-install hooks use a single export default define...(), which is one export.
Files inside src/types/ use plain <name>.ts (one PascalCase type per file) — no .type.ts suffix. Files inside src/<service>-client/ also use plain <name>.ts; the folder name carries the suffix, so do not repeat it on the file.
When field definitions differ only by objectUniversalIdentifier and label, replace them with a factory in src/fields/field-factories.ts.
Read secrets through the application-config helper, not raw process.env.
create-app first when the app does not exist.Once all edits for the change are complete, run lint and typecheck once at the end (not after each individual edit), then sync the app to verify the definitions are valid:
yarn twenty dev:typecheck
yarn lint
yarn twenty dev --once
yarn twenty dev:typecheck checks generated app types and yarn lint checks local lint rules. yarn twenty dev --once then builds the app and pushes entity definitions to the active remote; if any definition is invalid, the sync reports the error. Run all three a single time once every edit is done, not repeatedly after each step.
When the user explicitly asks to run tests, follow tests.md: unit tests may use the package's unit-test script, and the full suite must run with TWENTY_API_URL=http://localhost:2021 against the isolated test instance.
Switch to manage-app and use ../manage-app/cli-and-sync.md for sync or remote troubleshooting.
Tests are not part of sync validation but should cover src/utils/ helpers and post-install hook idempotency. See tests.md.