Skip to main content
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.

In this section

Objects

defineObject — declare new record types with their own fields.

Extending Objects

defineField — add fields to standard or other apps’ objects.

Relations

Bidirectional MANY_TO_ONE / ONE_TO_MANY connections between objects.

Entities at a glance

The SDK detects these via AST analysis at build time, so file organization is up to you — the convention is src/objects/, src/fields/, and src/indexes/. Stable universalIdentifier UUIDs tie everything together across deploys.

Indexes (optional)

Apps can ship indexes alongside their objects to keep recurring queries fast. The most common case is a status or foreign-key column that you read frequently.
src/indexes/post-card-status.index.ts

Unique indexes

defineIndex accepts isUnique: true for both single- and multi-column uniqueness. This is the recommended primitive — defineField({ isUnique: true }) is deprecated and will be removed in a future release.

Other constraints

  • Partial WHERE clauses stay under admin control — apps can’t declare them.
  • Each object is capped at 10 custom indexes (the framework’s own indexes don’t count).
Order the fields array the way Postgres should use it — leftmost column first, like a phone book. Indexes are not free: every write to the table updates them. Add one only when you have a query that needs it.
Looking for Application Config or Roles & Permissions? Those describe the app itself rather than the data it adds — they live under Config. Looking for Connections (Linear, GitHub, Slack OAuth)? Those exist to be called from logic functions and live under Logic.