Field Types
What a field type decides in ObjectOS — the column, the API, the form widget and what the AI Builder asks for — with a few examples and the full catalogue on objectstack.ai.
Full specification: Field types on objectstack.ai. This page is the summary: where ObjectOS uses it, and a few examples.
A field's type decides what it stores and how every surface treats it. There
are 48 built-in types — text, numbers, dates, choices, relations, computed
values, media, structured data and richer inputs such as addresses, ratings
and signatures — and every object also carries system fields (id,
created_at, updated_at, created_by, updated_by) you do not declare.
Where ObjectOS uses it
One field definition drives all of these:
*.object.ts (field spec)
│
├─► Postgres / MySQL / SQLite column + index + constraint
├─► REST: validated on POST/PATCH, exposed on GET
├─► UI: form widget + list column
├─► AI Builder: tool argument schema (so the AI knows what to ask)
└─► Audit: change-tracked if `trackHistory: true`- The database gets a column, its index and its constraints.
- The REST API validates the field on every write and returns it on read (REST API).
- The UI picks the form widget and the list column from the type.
- The AI Builder knows what to ask for, because the type is the argument schema of its tools.
- Audit records the field's changes when
trackHistoryis on.
Examples
A choice — a select field's options:
// Keys of a `select` field; its other keys are omitted.
options: [
{ value: 'low', label: 'Low' },
{ value: 'high', label: 'High', color: '#e02' },
{ value: 'urgent', label: 'Urgent', color: '#c00' }
]A relation — a lookup to an account, with the picker narrowed to active ones:
// One field.
{
type: 'lookup',
reference: 'account', // target object name
lookupFilters: [ // narrow the lookup picker
{ field: 'status', operator: 'eq', value: 'active' }
],
deleteBehavior: 'set_null' // 'set_null' | 'cascade' | 'restrict'
}A computed value — a formula field, written in CEL:
// One field.
{
name: 'profit_margin',
type: 'formula',
expression: F`(record.revenue - record.cost) / record.revenue * 100`
}An attachment — files, checked on the server:
// One field.
{
type: 'file',
multiple: true, // store an array of attachments
accept: ['image/png', 'image/jpeg'], // MIME types / extensions
maxSize: 10_000_000 // bytes
}Media
A media field stores an opaque sys_file id — not a URL, and not an
inline metadata blob. The { url, name, size, mimeType, … } object you see
when you read a record is the expanded form, derived from the owning sys_file
record; url comes from the /files/:fileId resolver.
Because the platform owns the bytes, accept and maxSize are not just hints
for the browser's file picker: the server re-checks every record write against
them, so a caller that talks to the API directly cannot bypass them. A download
carries the real filename and content type, and read access is delegated to
the owning object. Existing deployments convert with
os migrate files-to-references; see Storage for
where the bytes live and the
17.0 release notes for the
migration.
See also
- Build → Data model — composing fields into objects
- Relationships — lookups and master-detail
- ObjectQL — querying these fields
CEL Expressions
The expression language behind formula fields, validation rules, visibility, sharing and flow conditions — where ObjectOS uses it, a few examples, and the full language on objectstack.ai.
skills CLI
The npx command that installs the ObjectStack skills into your coding agent — where ObjectOS uses it, a few examples, and the full skill reference on objectstack.ai.