Fields and field options
Fields define what data an app stores and how that data appears in the UI. Every app exports an AppField[] array from fields/index.ts. The engine reads this array at startup and builds the schema, form controls, and validation rules from it.
This page covers data types, the type system, field options, calculated fields, and naming conventions.
Data types
Each field has a type property drawn from one of three unions defined in api/schema/typings/schema-field.ts.
DataType - the primary data type:
text · lookup · lookupMulti · appsLookup · linkto · linkslist · fileslist · peoplelist · date · datetime · person · bool · number · object
TextType - sub-type used when type is 'text':
string · keyword · text · richtext
NumberType - sub-type used when type is 'number':
unknown · float · integer
CalcType - for computed fields (see Calculated fields below):
calcfield · rollup · datarollup · treerollup · shadow · tablecalcfield
A field that stores a single-line string uses type: 'text' with a text sub-type of 'string'. A field that stores a currency amount uses type: 'number' with a number sub-type of 'float' and a decimal_places option.
Type system
The engine generates TypeScript types from field definitions so that record access is fully typed:
const { entity, entityOld, records } = makeEntity(fields, actions);
// entity.title, entity.state - fully typed based on field definitions
Key type utilities:
FieldName<Entity>- all accessible field namesEditableFieldName<Entity>- writable fields onlyfieldName__resolved- expands to{ caption, position }for lookup fieldsfieldName__list- expands toArray<ListEntry>for list-type fields
Use these utilities in actions and view logic to keep field references type-safe. The compiler will catch misspelled or removed field names at build time.
Field options
The AppField.options object controls rendering, validation, and behavior. There are 50+ available options. The table below lists the most commonly used ones.
Form controls and rendering
| Option | Type | Purpose |
|---|---|---|
custom_form_control | string | Override the default form widget: 'search-box', 'drop-down-simple', 'color', 'link-list', etc. |
gridview_renderer | string | Custom list/grid cell renderer: 'circle_percent', 'indicator', 'rich_text_line', etc. |
plain_renderer | string | Custom plain-view renderer: 'colorful-lookup', 'avatar-image', 'work-progress-bar', etc. |
code_editor_enabled | boolean | Show a code editor instead of a text input |
code_editor_language | string | Language for syntax highlighting: 'javascript', 'json', 'html', etc. |
Validation and input
| Option | Type | Purpose |
|---|---|---|
restrict_input | string | Input validation pattern: 'email', 'percent', 'url', 'phone', 'int' |
input_mask | string/object | Input mask (e.g. phone format) |
max_length | number | Maximum character count |
decimal_places | number | Decimal precision for number fields |
number_min_value / number_max_value | number | Numeric range validation |
Defaults and help text
| Option | Type | Purpose |
|---|---|---|
default_value_expression | string/bool/number | Default value applied to new records |
help_text | LanguageTextObject | Tooltip/help text shown on the field |
placeholder_text | LanguageTextObject | Placeholder text in the input |
Lookup display
| Option | Type | Purpose |
|---|---|---|
lookup_entries_formatting | object | Per-lookup-entry colors, icons, and text colors |
lookup_entries_localization | object | Per-lookup-entry translations |
Access and lifecycle
| Option | Type | Purpose |
|---|---|---|
access | string | Field access level: 'workspaceAdmin', 'workspaceTeam', 'groups' |
changeset_hide | boolean | Hide field changes from the change history |
clear_on_transition | boolean | Clear the value when an action fires |
conditional_formatting | string | Dynamic formatting function name |
Options are additive. A field with no options uses the engine's default widget and validation for its data type. Add options only when you need to override that default.
Display formatting
| Option | Type | Purpose |
|---|---|---|
formatting_pre | LanguageTextObject | Prefix displayed before the value (e.g., "$") |
formatting_post | LanguageTextObject | Suffix displayed after the value (e.g., "h", "kg") |
special_decimal_format | string | Special numeric formatting: 'HH:MM' renders decimals as hours:minutes |
Schema type markers
When the API returns a schema (via /api/schema/{WS}!{APP}), each field includes an ed_custom_field_type property that identifies how the field behaves in the system:
| Schema type | Meaning | Example fields |
|---|---|---|
authorinfo | Person field (stores user GUID) | keeper_id, created_by_account_id |
lookup_custom | Lookup with custom values | state, priority |
lookup_link_to_entity | Link to another record | parent_id, c_related_task |
linklist | Multi-link field | record_links |
peoplelist | Multi-person field | assignees_list |
file / filelist | File attachment field | attachments |
richtext | Rich text (Lexical editor) | description |
These markers determine how the API resolves values during read and write operations. For example, authorinfo fields accept both user GUIDs and full names - the API resolves names to GUIDs automatically.
Multi-language support
Field captions and several text options support multiple languages through the LanguageTextObject type. Supported language codes are defined by CaptionLanguage:
type CaptionLanguage = 'en' | 'uk' | 'ru' | 'zh' | 'de' | 'fr' | 'es';
Use a language map anywhere the engine expects translatable text:
caption: { en: 'Priority', uk: 'Пріоритет' }
The following field properties accept LanguageTextObject values: caption, help_text, placeholder_text, formatting_pre, formatting_post, and default value expressions.
Calculated fields
Calculated fields derive their values from other fields or from related records. They use one of the CalcType values instead of a DataType.
| Calc type | Behavior | Execution |
|---|---|---|
calcfield | Simple formula over the current record's fields | Synchronous |
rollup | Aggregate value from child records | Asynchronous (Processor) |
datarollup | Data-level rollup | Asynchronous (Processor) |
treerollup | Aggregate across a tree hierarchy | Asynchronous (Processor) |
shadow | Denormalized copy of a field from a linked record | Asynchronous (Processor) |
tablecalcfield | Editable grid calculation | Asynchronous (Processor) |
Simple calcfield formulas execute synchronously when the record loads. All other calc types are asynchronous - the background Processor evaluates them after the triggering change is saved.
Calc field definitions live in fields/calc-fields/index.ts. The build system bundles this directory separately so the formulas can run in the browser without pulling in server-only code. See Package structure for where this fits in the directory layout.
Field naming conventions
Custom fields use the c_ prefix - for example c_priority, c_due_date, c_total_cost. This prefix distinguishes app-specific fields from system fields.
System fields are provided by @comind/base and have no prefix: title, description, state, creation_date, update_date, keeper_id, and so on. Every app inherits these automatically.
When referencing fields in actions, view logic, or form layouts, always use the db name (c_priority, not the caption). The TypeScript type system will enforce correct names at compile time.