ServerDynamic Objects
Schema Runtime
Entity discovery, validation caching, formula computation, and schema versioning.
Entity Discovery
When a request targets a dynamic object, the system determines if it's a "Static" (Prisma) or "Dynamic" model:
// Discovery flow
const isDynamic = !prisma[modelName]; // Check if not static Prisma model
if (isDynamic) {
const fields = await EntityMetadataService.getFields(entity, tenantId);
// Return dynamic field definitions
}Field Metadata
Each dynamic entity has associated Field records containing:
name: Field API nametype: Field type (TEXT, NUMBER, LOOKUP, etc.)label: UI labelrequired: Whether field is mandatorydefaultValue: Default value expressionoptions: Picklist options array
Validation Caching
Generating validation schemas is expensive. The ValidationService caches generated AJV validators:
// Double-key cache: (tenantId, entityName)
const cacheKey = `${tenantId}:${entityName}`;
const validator = cache.get(cacheKey) ?? generateValidator(fields);Cache Invalidation
Caches must be invalidated when:
- An
Entitydefinition is modified - A
Fieldis added, removed, or updated - A tenant's metadata is reset
This is handled automatically via afterSuccess hooks on Entity and Field CRUD models.
Verification Endpoint
To debug validation issues:
# Get current schema as seen by server
GET /api/v1/metadata/fields?entity=InvoiceFormula Runtime
Formula fields are computed at runtime during the execution phase:
// Formula evaluation
for (const field of formulaFields) {
const computed = evaluateFormula(field.formula, recordData);
recordData[field.name] = computed;
}Supported Formula Functions
- Mathematical:
SUM,AVG,MIN,MAX,ROUND - Logical:
IF,AND,OR,NOT - Text:
CONCAT,LEFT,RIGHT,LEN - Date:
TODAY,NOW,DATEADD - Lookup:
LOOKUP
Formula Context
Formulas have access to:
- Current record fields
- Related record fields (via LOOKUP)
- System values (
$USER,$TODAY)
Schema Versioning
Each dynamic entity maintains a schema version that increments on field changes:
-- Entity table has version column
SELECT version FROM entity WHERE name = 'Invoice'; -- 42
-- Field changes increment version
UPDATE entity SET version = version + 1 WHERE name = 'Invoice';The client uses this version for cache invalidation and schema comparison.