ObjectStackObjectStack

Type System

Complete reference for ObjectQL field types - Scalars, relationships, computed fields, and advanced types

ObjectQL provides 20+ specialized field types that encode business semantics, not just data storage primitives. Each type understands its purpose and automatically configures database schemas, UI renderers, validation rules, and API serialization.

Type Philosophy

Traditional databases:

-- Just stores data
CREATE TABLE customer (
  revenue DECIMAL(18,2)  -- Is this USD? EUR? Monthly? Annual?
);

ObjectQL:

# Encodes business meaning
revenue:
  type: currency
  label: Annual Revenue
  scale: 2
  precision: 18
  # Automatically knows:
  # - Store amount + currency code
  # - UI shows currency symbol
  # - Validate numeric precision
  # - Format for display ($1,234.56)

Type Categories

Scalar Types

Primitive values: text, numbers, dates, booleans

Relationship Types

Lookups, master-detail, hierarchical (tree) references

Computed Types

Formulas, rollup summaries, auto-numbers

Complex Types

JSON, tags, geolocation, file attachments


1. Scalar Types

Text Types

text

Single-line text field.

company_name:
  type: text
  label: Company Name
  maxLength: 255
  required: true
  searchable: true

Database mapping:

  • PostgreSQL: VARCHAR(maxLength) or TEXT
  • MongoDB: String
  • Redis: String

UI rendering:

<input type="text" maxlength="255" required />

Use cases:

  • Names, titles, identifiers
  • Email addresses, phone numbers
  • Short descriptions

textarea

Multi-line plain text.

description:
  type: textarea
  label: Description
  maxLength: 5000

Database mapping:

  • PostgreSQL: TEXT
  • MongoDB: String

UI rendering:

<textarea rows="10" maxlength="5000"></textarea>

Use cases:

  • Comments, notes
  • Descriptions
  • Plain text content

html

Rich text with HTML markup.

bio:
  type: html
  label: Biography

Database mapping:

  • PostgreSQL: TEXT
  • MongoDB: String

UI rendering:

<!-- Rich text editor (TinyMCE, Quill, etc.) -->
<div class="rich-text-editor"></div>

Use cases:

  • Blog posts, articles
  • Product descriptions
  • Email templates

email

Email address with validation.

email:
  type: email
  label: Email Address
  required: true
  unique: true
  index: true

Validation:

  • RFC 5322 compliant regex
  • Lowercase normalization
  • Domain validation (optional DNS check)

Database mapping:

  • PostgreSQL: VARCHAR(255)
  • MongoDB: String

Use cases:

  • User emails
  • Contact information
  • Notification addresses

url

URL with protocol validation.

website:
  type: url
  label: Website

Validation:

  • Must include protocol (http://, https://)
  • Valid domain format
  • Optional reachability check

Use cases:

  • Company websites
  • Social media links
  • API endpoints

phone

Phone number with international format.

phone:
  type: phone
  label: Phone Number
  format: international  # E.164 format

Storage format: E.164 (+12025551234)

Display format: (202) 555-1234

Validation:

  • Valid phone number format
  • Country code verification
  • Optional SMS capability check

Numeric Types

number

General-purpose numeric field.

quantity:
  type: number
  label: Quantity
  min: 0
  max: 9999
  scale: 0  # Integer
  defaultValue: 1

Configuration:

  • scale: Decimal places (0 = integer)
  • precision: Total digits
  • min/max: Range validation

Database mapping:

  • PostgreSQL: NUMERIC(precision, scale)
  • MongoDB: Number or Decimal128

Use cases:

  • Quantities, counts
  • Ratings, scores
  • General measurements

currency

Money amount with currency code.

annual_revenue:
  type: currency
  label: Annual Revenue
  currencyConfig:
    precision: 2
    currencyMode: fixed
    defaultCurrency: USD

Storage:

{
  "value": 1234.56,
  "currency": "USD"
}

Features:

  • Multi-currency support
  • Automatic formatting ($1,234.56)
  • Exchange rate conversion (optional)
  • ISO 4217 currency codes

Database mapping:

  • PostgreSQL: NUMERIC(18,2) + VARCHAR(3) or JSONB
  • MongoDB: { value: Decimal128, currency: String }

percent

Percentage value (0-100).

discount_rate:
  type: percent
  label: Discount
  scale: 2
  min: 0
  max: 100

Display: 25.5% (automatically adds % symbol)

Storage: As decimal (0.255 for 25.5%)

Use cases:

  • Discounts, margins
  • Completion rates
  • Tax rates

Date/Time Types

date

Calendar date (no time component).

birth_date:
  type: date
  label: Date of Birth

Storage format: ISO 8601 (2024-01-15)

Database mapping:

  • PostgreSQL: DATE
  • MongoDB: Date (time set to 00:00:00 UTC)

Use cases:

  • Birthdays, anniversaries
  • Contract dates
  • Deadlines

datetime

Date and time with timezone.

meeting_time:
  type: datetime
  label: Meeting Time

Storage format: ISO 8601 with timezone (2024-01-15T14:30:00Z)

Database mapping:

  • PostgreSQL: TIMESTAMP WITH TIME ZONE
  • MongoDB: Date

time

Time of day (no date component).

business_hours_start:
  type: time
  label: Business Hours Start
  defaultValue: "09:00:00"

Storage format: HH:MM:SS — input accepts HH:MM or HH:MM:SS (with an optional fractional part and Z/offset). A time is a wall-clock value, not an instant: it is validated as a time-of-day, not parsed as a date.

Use cases:

  • Business hours
  • Recurring event times
  • Time-based triggers

Boolean Types

boolean

True/false value.

is_active:
  type: boolean
  label: Active
  defaultValue: true

Storage:

  • PostgreSQL: BOOLEAN
  • MongoDB: Boolean
  • Redis: 1 or 0

UI rendering:

<input type="checkbox" />
<!-- or -->
<select>
  <option value="true">Yes</option>
  <option value="false">No</option>
</select>

toggle

Boolean displayed as a toggle switch.

email_opt_in:
  type: toggle
  label: Subscribe to Newsletter
  defaultValue: false

Difference from boolean:

  • Always renders as a toggle switch (not a dropdown)
  • Typically used for consent, preferences, on/off settings

Selection Types

select

Dropdown/picklist from predefined options.

priority:
  type: select
  label: Priority
  options:
    - value: low
      label: Low
      color: green
    - value: medium
      label: Medium
      color: yellow
    - value: high
      label: High
      color: orange
    - value: critical
      label: Critical
      color: red
  defaultValue: medium
  required: true

Storage: Stores value (not label)

Database mapping:

  • PostgreSQL: VARCHAR or ENUM
  • MongoDB: String

Use cases:

  • Status, stage, priority
  • Categories, types
  • Fixed value lists

multiselect

Multiple selection from options.

tags:
  type: multiselect
  label: Tags
  options:
    - { value: customer, label: Customer }
    - { value: partner, label: Partner }
    - { value: vendor, label: Vendor }
  # multiple: true # Implied by type

Storage:

  • PostgreSQL: TEXT[] (array) or JSONB
  • MongoDB: [String]

Example value: ['customer', 'partner']


radio

Radio button group (single selection).

contact_method:
  type: radio
  label: Preferred Contact Method
  options:
    - { value: email, label: Email }
    - { value: phone, label: Phone }
    - { value: sms, label: SMS }

Difference from select:

  • All options visible (no dropdown)
  • Better UX for 2-5 options

2. Relationship Types

lookup

Foreign key reference to another object.

account_id:
  type: lookup
  label: Account
  reference: account
  required: true
  referenceFilters:
    - "is_active = true"
  deleteBehavior: set_null  # or restrict, cascade

Storage: Stores id of referenced record

Query behavior:

// Automatic JOIN
const opportunities = await ObjectQL.query({
  object: 'opportunity',
  fields: ['name', 'account.company_name']  // Joins account
});

On Delete Options:

  • set_null: Set field to null when referenced record is deleted
  • restrict: Prevent deletion if references exist
  • cascade: Delete this record when referenced record is deleted

Required foreign keys. A required: true lookup cannot be nulled, so the default set_null automatically escalates to restrict on such a field — deleting the parent is refused with 409 DELETE_RESTRICTED (the response carries dependentObject and dependentCount) instead of a confusing "<field> is required" validation error. To delete the children along with the parent, set deleteBehavior: cascade explicitly. An explicit set_null or cascade is always honored as written.

Multiple lookups:

contacts:
  type: lookup
  reference: contact
  multiple: true  # Many-to-many

Database mapping:

  • PostgreSQL: UUID + Foreign Key constraint
  • MongoDB: ObjectId or String

master_detail

Strong parent-child relationship.

project_id:
  type: master_detail
  label: Project
  reference: project
  deleteBehavior: cascade # Enforced by type usually

Characteristics:

  • Ownership: Child cannot exist without parent
  • Cascade delete: Deleting parent deletes all children
  • Sharing inheritance: Child inherits parent's permissions
  • Rollup support: Parent can aggregate child data

Difference from lookup:

FeatureLookupMaster-Detail
DeletionConfigurableAlways cascade
PermissionsIndependentInherited
RequiredOptionalAlways required
Use caseLoose referenceOwnership

Example:

# Order (Master)
name: order

# Order Line Item (Detail)
name: order_line_item
fields:
  order_id:
    type: master_detail
    reference: order

tree

Hierarchical reference for parent-child trees.

parent:
  type: tree
  label: Parent
  reference: category

Storage: Stores the id of the referenced parent record.

Use cases:

  • Category / folder hierarchies
  • Org charts
  • Any self-referential tree

A single polymorphic field type (one field that can point at several different object types, e.g. Account or Contact) is not currently a built-in field type. The FieldType enum exposes lookup, master_detail, and tree; the reference property accepts a single target object name, not a list. Model "related to any object" use cases (activity feeds, attachments, comments) with separate lookups or an application-level type discriminator.


3. Computed Types

formula

Calculated field based on other fields.

total_price:
  type: formula
  label: Total Price
  expression: "record.quantity * record.unit_price"

Formula expressions use CEL (Common Expression Language). Fields on the current record are referenced via the record. scope. CEL syntax differs from spreadsheet/Salesforce formula languages:

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, >, <, >=, <=
  • Logical: &&, ||, !
  • Conditional: ternary cond ? a : b (there is no IF() function)
  • Functions: today(), daysFromNow(), daysBetween(), len(), size(), upper(), lower(), trim(), contains(), startsWith(), coalesce(), min(), max(), abs(), round(), and more (see the formula reference).

Examples:

# Simple calculation
discount_amount:
  type: formula
  expression: "record.price * (record.discount_rate / 100)"

# Conditional logic (CEL ternary)
status_label:
  type: formula
  expression: "record.is_active ? 'Active' : 'Inactive'"

# Cross-object formula (via expanded lookup)
account_revenue_tier:
  type: formula
  expression: "record.account.annual_revenue > 1000000 ? 'Enterprise' : 'SMB'"

# Date calculation
days_until_due:
  type: formula
  expression: "daysBetween(today(), record.due_date)"

summary (Rollup)

Aggregate child records in master-detail relationship.

# On Account object
total_opportunities:
  type: summary
  label: Total Opportunities
  summaryOperations:
    object: opportunity
    function: count

total_opportunity_value:
  type: summary
  label: Pipeline Value
  summaryOperations:
    object: opportunity
    field: amount
    function: sum

Summary Types:

  • count: Count child records
  • sum: Sum a numeric field
  • min: Minimum value
  • max: Maximum value
  • avg: Average value

Requirements:

  • Aggregates a child object that references this object (via its lookup/master_detail field)
  • Set relationshipField only when the child has more than one reference back to this object

Database implementation:

  • Materialized view (PostgreSQL)
  • Aggregation pipeline (MongoDB)
  • Background job recalculation

Performance:

  • Real-time: Recalculate on child write (slow)
  • Batch: Update every N minutes (stale data)
  • Hybrid: Increment/decrement on simple changes

autonumber

Auto-incrementing unique identifier.

case_number:
  type: autonumber
  label: Case Number
  autonumberFormat: "CASE-{0000}"

Example values: CASE-0001, CASE-0002, ...

Format tokens:

  • {0000}: Zero-padded number
  • {YYYY}: Year
  • {MM}: Month
  • {DD}: Day

Complex formats:

invoice_number:
  type: autonumber
  autonumberFormat: "INV-{YYYY}-{0000}"
  # Generates: INV-2024-0001, INV-2024-0002, ...

Database implementation:

  • PostgreSQL: SEQUENCE
  • MongoDB: Atomic counter collection

4. Complex Types

json

Unstructured JSON data.

metadata:
  type: json
  label: Metadata

Storage:

  • PostgreSQL: JSONB
  • MongoDB: Native object

Query support:

// Query JSON properties
const products = await ObjectQL.query({
  object: 'product',
  filters: [
    ['metadata.color', '=', 'red']
  ]
});

Use cases:

  • Product attributes (varying by category)
  • Integration payloads
  • User preferences
  • Dynamic configurations

tags

Simple list of free-form string tags.

tags:
  type: tags
  label: Tags

There is no generic array field type. To store multiple values, use tags (free-form strings), multiselect (multiple choices from options), or set multiple: true on a scalar/lookup field to store an array of that type.

Storage:

  • PostgreSQL: TEXT[] or JSONB
  • MongoDB: [String]

address

Structured address with geocoding.

billing_address:
  type: address
  label: Billing Address
  allowGeocoding: true  # Allow address-to-coordinate conversion

Structure:

{
  "street": "123 Main St",
  "city": "San Francisco",
  "state": "CA",
  "postalCode": "94105",
  "country": "USA",
  "countryCode": "US",
  "formatted": "123 Main St, San Francisco, CA 94105"
}

Database mapping:

  • PostgreSQL: JSONB or composite type
  • MongoDB: Embedded document

location

Geographic coordinates.

office_location:
  type: location
  label: Office Location

Storage:

{
  "latitude": 37.7749,
  "longitude": -122.4194
}

Query:

// Find nearby locations (within 10 miles)
const nearby = await ObjectQL.query({
  object: 'store',
  filters: [
    ['location', 'near', { lat: 37.7749, lng: -122.4194, distance: 10, unit: 'miles' }]
  ]
});

Database mapping:

  • PostgreSQL: POINT or GEOGRAPHY
  • MongoDB: GeoJSON

file

File attachment reference.

avatar:
  type: file
  label: Profile Picture
  fileAttachmentConfig:
    allowedMimeTypes: [image/png, image/jpeg]
    maxSize: 5242880  # 5MB
    storageProvider: s3

Storage:

{
  "filename": "profile.jpg",
  "content_type": "image/jpeg",
  "size": 1024000,
  "url": "https://cdn.example.com/files/abc123.jpg"
}

Storage backends:

  • local: Server filesystem
  • s3: Amazon S3
  • azure: Azure Blob Storage
  • gcs: Google Cloud Storage

Features:

  • Automatic upload handling
  • Content-type validation
  • Virus scanning (optional)
  • CDN integration

image

Image file with transformations.

product_image:
  type: image
  label: Product Image
  fileAttachmentConfig:
    allowedMimeTypes: [image/png, image/jpeg, image/webp]
    imageValidation:
      maxWidth: 4096
      maxHeight: 4096
      generateThumbnails: true
      thumbnailSizes:
        - { name: thumbnail, width: 150, height: 150, crop: true }
        - { name: large, width: 1200, height: 800, crop: false }

Features:

  • Image dimension validation (minWidth/maxWidth/minHeight/maxHeight, aspectRatio)
  • Thumbnail generation (generateThumbnails, thumbnailSizes)
  • EXIF handling (preserveMetadata, autoRotate)

Type Conversion Matrix

How types convert between databases:

ObjectQL TypePostgreSQLMongoDBRedis
textVARCHAR / TEXTStringString
numberNUMERICNumberString
currencyJSONBObjectString (JSON)
dateDATEDateString (ISO)
datetimeTIMESTAMPDateString (ISO)
booleanBOOLEANBoolean0 / 1
selectVARCHAR / ENUMStringString
lookupUUID + FKObjectIdString
formulaGENERATEDVirtualComputed
jsonJSONBObjectString (JSON)
locationPOINTGeoJSONString (JSON)

Sensitive Data: Masking & Encryption

There is no user-defined "custom type" registry. For sensitive values, use the dedicated secret field type, which encrypts on write and masks on read, and restrict reader access with field-level security (readable: false) or hidden.

# Reversible secret (API keys, DB passwords)
api_key:
  type: secret
  label: API Key

The per-field maskingRule and encryptionConfig properties were pruned from the Field schema in 2026-06 — they were declared surface with no runtime consumer. In 2026-07 the masking shapes were removed from the spec entirely (ADR-0056 D8); EncryptionConfigSchema remains in the System namespace as [EXPERIMENTAL] roadmap surface. See Security & Access Control for status.

Type Selection Guide

For text data:

  • Short, single-line → text
  • Multi-line → textarea
  • Rich formatting → html
  • Email address → email
  • URL → url
  • Phone → phone

For numbers:

  • General purpose → number
  • Money → currency
  • Percentage → percent
  • Unique ID → autonumber

For dates:

  • Date only → date
  • Date + time → datetime
  • Time only → time

For selections:

  • Fixed options → select
  • Multiple selections → multiselect
  • 2-5 visible options → radio
  • Yes/No → boolean or toggle

For relationships:

  • Loose reference → lookup
  • Parent-child → master_detail
  • Self-referential hierarchy → tree

For calculations:

  • Derived value → formula
  • Aggregate children → summary

For complex data:

  • Flexible schema → json
  • List of tags → tags (or multiple: true)
  • Geographic data → location or address
  • File upload → file or image

Next Steps

On this page