Skip to main content

Object Types

Object Types define the data model for your vertical application. They are typed schema definitions that describe the structure of your domain entities -- their fields, validation rules, relationships, and available actions. Object Types are defined in TypeScript, seeded to the Configurator service, and used by ResourceAPI at runtime for data validation.

What Object Types Are

An Object Type is similar to a database table definition, but richer. It specifies:

  • Properties -- The fields on each resource, with types, validation, and defaults
  • Actions -- Custom operations that can be executed on a resource (e.g., "submit", "approve")
  • Link Types -- Relationships to other resource types (e.g., an Application has many Documents)
  • Storage backend -- Where data is persisted (PostgreSQL or Cosmos DB)
  • Status -- Whether the type is published (active) or in draft

Schema Format

Object Types are defined in src/eai.config/object-types.ts and exported as a map of tenant slug to array of type definitions:

import { ObjectTypeDefinition } from './types';

export const objectTypes: Record<string, ObjectTypeDefinition[]> = {
'my-tenant': [
{
name: 'Application', // PascalCase (enforced by Configurator)
displayName: 'Application', // Human-friendly label for UI
description: 'Permit applications submitted by users',
properties: [/* ... */],
linkTypes: [/* ... */],
actions: [/* ... */],
storageBackend: 'postgresql', // postgresql | cosmosdb
status: 'published', // draft | published | deprecated
},
],
};

Field Types

Every property on an Object Type has a type that determines what data it can store. The platform supports 8 field types:

TypeDescriptionExample Use
textString valueNames, emails, addresses, identifiers
numberNumeric value (integer or float)Counts, amounts, scores
booleanTrue/false flagisVerified, isActive, isComplete
dateISO 8601 datetime stringsubmittedAt, createdAt, dueDate
selectEnum with predefined options (requires options array)Status, category, priority
multi-selectMultiple values from predefined optionsTags, categories, features
relationshipReference to another resource by IDparentId, ownerId, assigneeId
rich-textFormatted text content (HTML or Markdown)Descriptions, notes, comments

Property Definition

Each property is defined with the following fields:

interface PropertyDefinition {
name: string; // camelCase property name (unique within the type)
type: FieldType; // One of the 8 field types above
required?: boolean; // Whether the field must be provided on create
indexed?: boolean; // Add a database index for faster queries
defaultValue?: any; // Default value if not provided
options?: Array<{ // Required for 'select' and 'multi-select' types
label: string;
value: string;
}>;
description?: string; // Help text displayed in UI
}

Property Examples

properties: [
// Text field
{
name: 'applicantName',
type: 'text',
required: true,
description: 'Full legal name of the applicant',
},
// Number field
{
name: 'estimatedCost',
type: 'number',
required: false,
defaultValue: 0,
},
// Boolean field
{
name: 'isVerified',
type: 'boolean',
defaultValue: false,
},
// Date field
{
name: 'submittedAt',
type: 'date',
indexed: true,
},
// Select field (requires options)
{
name: 'status',
type: 'select',
required: true,
defaultValue: 'draft',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Submitted', value: 'submitted' },
{ label: 'Under Review', value: 'under-review' },
{ label: 'Approved', value: 'approved' },
{ label: 'Rejected', value: 'rejected' },
],
},
// Relationship field
{
name: 'assignedTo',
type: 'relationship',
description: 'Staff member assigned to review this application',
},
// Rich text field
{
name: 'notes',
type: 'rich-text',
description: 'Internal review notes',
},
],

Actions

Actions are custom operations that can be executed on a resource. They define state transitions with validation rules, required roles, and side effects. Actions are executed via the ResourceAPI action endpoint.

actions: [
{
name: 'submit',
displayName: 'Submit Application',
requiredRole: 'tenant-user',
validationRules: {
requiredFields: ['applicantName', 'applicantEmail'],
requiredStatus: 'draft',
},
sideEffects: [
{ type: 'set_field', field: 'status', value: 'submitted' },
{ type: 'set_timestamp', field: 'submittedAt' },
],
},
{
name: 'approve',
displayName: 'Approve Application',
requiredRole: 'tenant-staff',
validationRules: {
requiredStatus: 'under-review',
},
sideEffects: [
{ type: 'set_field', field: 'status', value: 'approved' },
{ type: 'set_timestamp', field: 'approvedAt' },
{ type: 'set_user', field: 'approvedBy' },
],
},
],

Side Effect Types

TypeDescriptionExample
set_fieldSet a field to a specific value{ type: 'set_field', field: 'status', value: 'submitted' }
set_timestampSet a field to the current ISO timestamp{ type: 'set_timestamp', field: 'submittedAt' }
set_userSet a field to the current user's ID{ type: 'set_user', field: 'reviewedBy' }

Link Types define relationships between Object Types. They enable you to model connections between resources, such as an Application having many Documents.

linkTypes: [
{
name: 'documents',
targetObjectType: 'Document', // PascalCase target type name
cardinality: 'one-to-many', // one-to-one | one-to-many | many-to-one | many-to-many
cascadeDelete: true, // Delete linked resources when parent is deleted
},
{
name: 'reviewer',
targetObjectType: 'StaffMember',
cardinality: 'many-to-one',
cascadeDelete: false,
},
],

Cardinality Options

CardinalityDescriptionExample
one-to-oneEach resource links to exactly one targetApplication -> PrimaryContact
one-to-manyEach resource links to many targetsApplication -> Documents
many-to-oneMany resources link to one targetApplications -> Reviewer
many-to-manyMany resources link to many targetsApplications -> Tags

Complete Example

Here is a complete Object Type definition for a permit application system:

export const objectTypes = {
'permits-tenant': [
{
name: 'Application',
displayName: 'Permit Application',
description: 'Building permit applications submitted by property owners',
properties: [
{ name: 'applicantName', type: 'text', required: true },
{ name: 'applicantEmail', type: 'text', required: true },
{ name: 'projectAddress', type: 'text', required: true, indexed: true },
{ name: 'estimatedCost', type: 'number', required: true },
{ name: 'projectDescription', type: 'rich-text' },
{
name: 'status',
type: 'select',
required: true,
defaultValue: 'draft',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Submitted', value: 'submitted' },
{ label: 'Under Review', value: 'under-review' },
{ label: 'Approved', value: 'approved' },
{ label: 'Rejected', value: 'rejected' },
],
},
{ name: 'isExpedited', type: 'boolean', defaultValue: false },
{ name: 'submittedAt', type: 'date', indexed: true },
{ name: 'approvedAt', type: 'date' },
{ name: 'approvedBy', type: 'relationship' },
],
linkTypes: [
{
name: 'documents',
targetObjectType: 'Document',
cardinality: 'one-to-many',
cascadeDelete: true,
},
{
name: 'inspections',
targetObjectType: 'Inspection',
cardinality: 'one-to-many',
cascadeDelete: false,
},
],
actions: [
{
name: 'submit',
displayName: 'Submit for Review',
requiredRole: 'tenant-user',
validationRules: {
requiredFields: ['applicantName', 'applicantEmail', 'projectAddress'],
requiredStatus: 'draft',
},
sideEffects: [
{ type: 'set_field', field: 'status', value: 'submitted' },
{ type: 'set_timestamp', field: 'submittedAt' },
],
},
{
name: 'approve',
displayName: 'Approve',
requiredRole: 'tenant-staff',
validationRules: { requiredStatus: 'under-review' },
sideEffects: [
{ type: 'set_field', field: 'status', value: 'approved' },
{ type: 'set_timestamp', field: 'approvedAt' },
{ type: 'set_user', field: 'approvedBy' },
],
},
],
storageBackend: 'postgresql',
status: 'published',
},
],
};

Validation Rules

RuleDescription
Object type name must be PascalCaseConfigurator auto-generates a kebab-case slug from the name
Property name must be unique within the typecamelCase is recommended
options array is required for select and multi-select typesEach option has label and value
storageBackend defaults to postgresqlUse cosmosdb for document-heavy workloads
status must be published for the type to be activedraft types are not available via ResourceAPI

Validating and Seeding

Use the EAI CLI to validate your object type definitions and seed them to Configurator:

# Validate object type definitions locally (checks schema, naming, required fields)
eai types validate

# Seed object types to Configurator (creates or updates types in the platform)
eai types seed

# Seed types for a specific tenant
eai types seed --tenant my-tenant

The seed process pushes your TypeScript definitions to Configurator via the PublicAPI orchestrate endpoint. Once seeded, ResourceAPI uses these schemas to validate all incoming data for that tenant.

caution

Seeding is additive by default. Existing types are updated, not deleted. If you rename a type, the old type will remain in Configurator and must be removed manually.