Models API
The Models API (dcupl.models) provides methods for managing data model definitions in dcupl. Models define the structure, types, and relationships of your data.
Methods
set()
Adds a model definition. dcupl builds the model on the next init() or update().
If a model with the same key already exists, set() replaces its definition (SDK 2.0): a changed attribute takes effect and an attribute left out of the new definition is removed. To add to a definition instead, use update(). dcupl stores a copy, so later changes to the object you passed do not reach the instance. After init(), the change takes effect on the next dcupl.update(), which rebuilds all data like init() (a full update), and lists created earlier serve the new definition. Define each property only once within a definition: a property key given twice adds a second definition and reports a DuplicatePropertyKey quality error. Model files loaded by the app loader are the exception: files with the same key are still merged (first declaration wins).
set() does not throw on an invalid definition. A model with an empty key or keyProperty (or a malformed autoGenerateProperties, properties or references) is not registered, and a property with an unknown type is left out of the model; each case records an InvalidModelDefinition quality error, which stays until a valid set() for the same key replaces the definition.
Signature:
set(model: ModelDefinition): voidParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
ModelDefinition |
Yes | The model definition to set |
Examples:
dcupl.models.set({
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
],
});dcupl.models.set({
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
],
references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
});dcupl.models.set({
key: 'Product',
keyProperty: 'id',
autoGenerateKey: false,
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
{ key: 'tags', type: 'Array<string>', separator: ',' },
],
references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
quality: {
enabled: true,
attributes: {
required: true,
},
},
});update()
Merges a definition into an existing model: properties and references are appended, scalar fields of the later definition win, and an attribute keeps its first declaration. The change is applied on the next dcupl.update(), which rebuilds all data like init(); get() then returns the merged definition. Use set() to replace a definition.
Signature:
update(model: ModelDefinition): voidParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
ModelDefinition |
Yes | The model definition to merge |
Examples:
dcupl.models.update({
key: 'Product',
properties: [{ key: 'description', type: 'string' }],
});dcupl.models.update({
key: 'Product',
references: [{ key: 'manufacturer', type: 'singleValued', model: 'Manufacturer' }],
});get()
Retrieves the definition of a built model by key. It returns undefined for a model that is not built yet: before init(), or for a key first set after init() until the next dcupl.update(). For a built model it returns the definition the model was built from; a set() or update() on it shows in get() only after the next dcupl.update(). A set() with an invalid definition unregisters the model at once, so get() returns undefined. For a model with autoGenerateProperties, the inferred properties are included. The result is a copy: changing it has no effect on the instance (edit a model with get → change → set() → dcupl.update()). Inline data given in the definition is not included.
Signature:
get(modelKey: string): ModelDefinition | undefinedParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
modelKey |
string |
Yes | The key of the model to retrieve |
Returns: ModelDefinition | undefined - The model definition or undefined if the model is not built
Examples:
const model = dcupl.models.get('Product');
if (model) {
console.log(model.properties);
console.log(model.references);
}const exists = dcupl.models.get('Product') !== undefined;const model = dcupl.models.get('Product');
if (model) {
const properties = model.properties || [];
const references = model.references || [];
}remove()
Unregisters a model at once (SDK 2.0): its definition, its data (rows from data.* or inline data) and its lists. Queries on it throw, its errors leave quality.errors.summary(), and the next init() or update() rebuilds all data like init() without bringing it back; references from other models then resolve as to a missing model (MissingModel). A later set() registers the model again; pass its data again.
Signature:
remove(modelKey: string): voidParameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
modelKey |
string |
Yes | The key of the model to remove |
Examples:
dcupl.models.remove('Product');['Product', 'Category', 'Tag'].forEach((key) => {
dcupl.models.remove(key);
});keys()
Returns the keys of all built models. dcupl's internal $Dcupl… models are left out.
Signature:
keys(): string[]Returns: string[] - Array of model keys
Examples:
const modelKeys = dcupl.models.keys();
console.log(modelKeys); // ['Product', 'Category', 'Tag']const hasProduct = dcupl.models.keys().includes('Product');const modelCount = dcupl.models.keys().length;Type definitions
ModelDefinition
Defines the structure of a data model.
Type Definition:
type ModelDefinition<ModelNames extends string = string> = {
key: ModelNames;
properties?: Property[];
references?: Reference[];
data?: RawItem[];
keyProperty?: string | string[];
keyPropertySeparator?: string;
autoGenerateKey?: boolean;
autoGenerateProperties?: boolean | { deep?: boolean; sampleSize?: number };
supportsAutoCreation?: boolean;
valueMappings?: ValueMappingConfig[];
meta?: ModelMetadata;
quality?: ModelQualityConfig;
};Properties:
| Property | Type | Description |
|---|---|---|
key |
string |
Unique model identifier |
properties |
Property[] |
Array of property definitions |
references |
Reference[] |
Array of reference definitions |
data |
RawItem[] |
Optional inline data for the model |
keyProperty |
string | string[] |
Property to use as item key (default: 'key'); an array builds a composite key |
keyPropertySeparator |
string |
Separator joining the parts of a composite key (default: '::') |
autoGenerateKey |
boolean |
Auto-generate unique keys for items without one (default: false) |
autoGenerateProperties |
boolean | object |
Infer property definitions from the data (default: false), see below |
supportsAutoCreation |
boolean |
Allow automatic item creation when referenced by other models (default: false) |
valueMappings |
ValueMappingConfig[] |
Transform values during data loading |
meta |
ModelMetadata |
Metadata attached to the model (name is required, other keys are free) |
quality |
ModelQualityConfig |
Quality validation configuration |
Examples:
const model: ModelDefinition = {
key: 'Product',
properties: [{ key: 'name', type: 'string' }],
};const model: ModelDefinition = {
key: 'Product',
keyProperty: 'productId',
autoGenerateKey: false,
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
{ key: 'createdAt', type: 'date', inputFormat: 'yyyy-MM-dd' },
],
references: [
{ key: 'category', type: 'singleValued', model: 'Category' },
{ key: 'tags', type: 'multiValued', model: 'Tag' },
],
quality: {
enabled: true,
attributes: {
required: true,
},
},
};Model options
keyProperty
Specifies which property in your data should be used as the unique item identifier. By default, dcupl looks for a property named key.
const model: ModelDefinition = {
key: 'Product',
keyProperty: 'productId', // Use 'productId' instead of 'key'
properties: [
{ key: 'productId', type: 'string' },
{ key: 'name', type: 'string' },
],
};
// Data can now use 'productId' as the identifier
dcupl.data.set(
[
{ productId: 'SKU-001', name: 'Widget' },
{ productId: 'SKU-002', name: 'Gadget' },
],
{ model: 'Product' },
);An array of property names builds a composite key: dcupl joins the values in array order with keyPropertySeparator (default '::') and writes the result to the item's key. Each value is converted with String(), so a missing part becomes 'undefined' (or 'null') in the key.
dcupl.models.set({
key: 'Translation',
keyProperty: ['sku', 'lang'],
properties: [
{ key: 'sku', type: 'string' },
{ key: 'lang', type: 'string' },
{ key: 'title', type: 'string' },
],
});
dcupl.data.set([{ sku: 'A1', lang: 'en', title: 'Widget' }], { model: 'Translation' });
await dcupl.init();
dcupl.query.one({ modelKey: 'Translation', itemKey: 'A1::en' }); // { key: 'A1::en', sku: 'A1', lang: 'en', title: 'Widget' }A row with no value for the key property (undefined or null; for a composite key: every part) is dropped and reported as a MissingKey quality error. On a model with derived, grouped, resolved or query references, or derived or expression properties, such a row currently also makes init(), or an update() that rebuilds (for example after data.set(), or after upsert() on a model with query references), log "Initialization failed" or "Update failed" and resolve false. Processing stops at that row, so remove such rows before loading. After init(), a keyless row in an upsert() that dcupl.update() applies on the partial path is currently dropped without the error; the next full rebuild reports it, and on those models resolves false. A composite key with only some parts missing is kept, with 'undefined' or 'null' for those parts. An empty string counts as a value: a blank key cell in a CSV file gives the key '' and the row is kept; further rows with a blank key replace it and are reported as NonUniqueKey.
autoGenerateKey
When true, dcupl generates a unique 10-character key for items that don't have one. Useful when your data doesn't have natural identifiers.
const model: ModelDefinition = {
key: 'LogEntry',
autoGenerateKey: true,
properties: [
{ key: 'message', type: 'string' },
{ key: 'timestamp', type: 'date' },
],
};
// Data without keys - dcupl generates them automatically
dcupl.data.set(
[
{ message: 'User logged in', timestamp: '2025-01-15' },
{ message: 'User logged out', timestamp: '2025-01-15' },
],
{ model: 'LogEntry' },
);
// Items will have auto-generated keys like 'a8f3k2m9x1'autoGenerateProperties
When true, dcupl infers property definitions from the data structure. Useful for quick prototyping or when working with dynamic data.
const model: ModelDefinition = {
key: 'DynamicData',
autoGenerateProperties: true,
// No properties defined - they will be inferred from data
};
dcupl.data.set([{ key: 'item1', name: 'Test', count: 42, active: true }], { model: 'DynamicData' });
// Properties 'name', 'count', 'active' are automatically createdautoGenerateProperties accepts true, { deep: true } (scan every row), { sampleSize: N } (scan up to N rows) or { deep: false } (the first row and nothing else). With true the first row names the properties and their types, and a column it types as a number is checked against every row: a later decimal makes it float, a later zero-padded value makes it string. In SDK 2.0, string values are inferred too: '19.99' becomes float, '3' becomes int, 'true' / 'false' (in any case) become boolean, '1' / '0' stay int, and a strict ISO-8601 date becomes date, so CSV columns are not all string. The key column stays string. A digit string is a number only when the number keeps every character: zero-padded values ('007', a zip code '01234') and integers beyond Number.MAX_SAFE_INTEGER are inferred as string, and so is a column that mixes them with numbers. Properties you declare in properties keep their declared type, so declare one where you need a specific type (for example int for months '01' to '12', which reads '07' as 7).
dcupl.models.set({ key: 'Row', autoGenerateProperties: { deep: true } });
dcupl.data.set(
[
{ key: 'r1', price: '5' },
{ key: 'r2', price: '19.99' },
],
{ model: 'Row' },
);
await dcupl.init();
// price is inferred as 'float', with true as well (number columns are checked against every row).
// With { deep: false } only the first row counts: price becomes 'int' and '19.99' is stored as 19A boolean property, declared or inferred, converts the strings 'true', 'false', '1' and '0' (case-insensitive, no trimming) and the numbers 1 and 0 to booleans. Any other string, number, object or array is reported as WrongDataType and the value stays undefined, so 'yes', 'no', '' or 2 do not load as a boolean. Inline data, JSON and CSV follow the same rule; an empty CSV cell counts as no value, not as ''. Map other spellings with valueMappings, or declare the column as string:
dcupl.models.set({
key: 'Row',
properties: [{ key: 'ok', type: 'boolean' }],
valueMappings: [
{
attributes: ['ok'],
values: [
{ from: ['yes', 'Y'], to: true },
{ from: ['no', 'N'], to: false },
],
},
],
});supportsAutoCreation
When true, items are automatically created in this model when referenced by other models. This is useful for lookup tables or reference data that might be incomplete.
const productModel: ModelDefinition = {
key: 'Product',
references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
data: [
{ key: 'p1', category: 'electronics' },
{ key: 'p2', category: 'books' },
],
};
const categoryModel: ModelDefinition = {
key: 'Category',
supportsAutoCreation: true, // Categories created automatically
properties: [{ key: 'name', type: 'string' }],
};
// After init, 'electronics' and 'books' exist in Category model
// even though no Category data was explicitly providedvalueMappings
Transform values during data loading. Useful for normalizing data, handling special values, or converting between formats.
Type Definition:
type ValueMappingConfig = {
attributes: string[]; // Properties/references to apply mapping to
values: {
from: (string | '$dcupl_falsy')[]; // Values to match
to: any; // Replacement value
}[];
};Mappings run on the raw value, before type conversion. CSV files loaded by DcuplAppLoader are the exception: the CSV parser converts each cell to the declared type first, and a boolean column accepts only true / false / 1 / 0 (in any case), so Yes / No are dropped before a mapping sees them (convert them in a loader transformer instead). A from value matches only with strict equality ('0' does not match the number 0). A from list that contains '$dcupl_falsy' matches only undefined, null and ''; other entries in that list are ignored, so put them in a separate values entry.
const model: ModelDefinition = {
key: 'Product',
valueMappings: [
{
attributes: ['category', 'brand'],
values: [
{
from: ['$dcupl_falsy'], // Matches undefined, null, empty string
to: 'uncategorized',
},
],
},
],
references: [
{ key: 'category', type: 'singleValued', model: 'Category' },
{ key: 'brand', type: 'singleValued', model: 'Brand' },
],
};const model: ModelDefinition = {
key: 'Product',
valueMappings: [
{
attributes: ['inStock'],
values: [
{ from: ['Yes', 'yes', 'Y', '1'], to: true },
{ from: ['No', 'no', 'N', '0'], to: false },
],
},
],
properties: [{ key: 'inStock', type: 'boolean' }],
};meta
Attach arbitrary metadata to the model for documentation or application-specific purposes.
const model: ModelDefinition = {
key: 'Product',
meta: {
name: 'Product Catalog',
description: 'All products available for sale',
version: '2.0',
maintainer: 'catalog-team@example.com',
},
properties: [{ key: 'name', type: 'string' }],
};
// Access metadata
const productModel = dcupl.models.get('Product');
console.log(productModel?.meta?.description);Property
Defines a property (field) in a model.
Type Definition:
type Property =
| PropertyString
| PropertyStringArray
| PropertyInt
| PropertyIntArray
| PropertyFloat
| PropertyFloatArray
| PropertyDate
| PropertyDateArray
| PropertyJson
| PropertyBoolean
| PropertyAny;Common Properties:
| Property | Type | Description |
|---|---|---|
key |
string |
Unique property identifier |
type |
PropertyType |
Data type of the property |
index |
boolean |
Build the property's lookup index at load time instead of on the first query that needs it (ignored on expression properties) |
expression |
string |
Computed property expression |
derive |
DeriveConfig |
Derive value from reference |
quality |
AttributeQualityConfig |
Validation rules |
filter |
boolean | object |
List the property in catalog.filters (filter UI items) |
origin |
string |
Read the value from a differently named source field, e.g. origin: 'Product Title' for a CSV column header |
meta |
ModelMetadata |
Metadata attached to the property |
aggregate |
boolean | object |
Accepted and returned by models.get(); no effect on queries or fn.aggregate() |
Type-specific options:
| Option | Types | Description |
|---|---|---|
separator |
Array<…> |
Splits a string value into array elements (default: ',') |
fractionDigits |
float, Array<float> |
Rounds the value to this many decimals |
inputFormat |
date, Array<date> |
Format of the raw value, e.g. 'yyyy-MM-dd' or 'dd.MM.yyyy'; 't' = Unix seconds, 'T' = Unix milliseconds |
UTC |
date, Array<date> |
Treat the parsed date and time as UTC instead of local time |
Property Types:
| Type | Description |
|---|---|
'string' |
Text values |
'Array<string>' |
Array of text values |
'int' |
Integer numbers |
'Array<int>' |
Array of integers |
'float' |
Floating point numbers |
'Array<float>' |
Array of floats |
'date' |
Date/time values |
'Array<date>' |
Array of dates |
'boolean' |
Boolean values: the strings true/false/1/0 (any case) and the numbers 1/0 convert, anything else is a WrongDataType |
'json' |
JSON objects |
'any' |
Any type |
Examples:
{
key: 'name',
type: 'string'
}{
key: 'price',
type: 'float',
fractionDigits: 2
}{
key: 'tags',
type: 'Array<string>',
separator: ','
}{
key: 'createdAt',
type: 'date',
inputFormat: 'yyyy-MM-dd',
UTC: true
}{
key: 'categoryName',
type: 'string',
derive: {
localReference: 'category',
remoteProperty: 'name'
}
}{
key: 'displayName',
type: 'string',
expression: '${brand} ${name}'
}{
key: 'email',
type: 'string',
quality: {
required: true,
validators: {
email: { value: true }
}
}
}Reference
Defines a relationship to another model.
Type Definition:
// GroupedRefeference is spelled this way in @dcupl/common
type Reference = ReferenceBase | ResolvedReference | DerivedReference | GroupedRefeference | QueryReference;Common Properties:
| Property | Type | Description |
|---|---|---|
key |
string |
Unique reference identifier |
type |
'singleValued' | 'multiValued' |
Single or multiple items |
model |
string |
Target model key |
separator |
string |
Separator for multi-valued references |
validity |
'loose' | 'strict' |
'loose' (default) keeps a reference to a key missing in the target model; 'strict' drops it |
filter |
boolean | object |
List the reference in catalog.filters (filter UI items) |
origin |
string |
Read the key(s) from a differently named source field |
quality |
AttributeQualityConfig |
Only required and nullable apply (AttributeQualityConfig): a null is kept and reported as NullValue unless nullable; a missing value is an UndefinedValue unless required: false |
meta |
ModelMetadata |
Metadata attached to the reference (returned by models.get()) |
index |
boolean |
Accepted, no effect: a reference's lookup index is always built at load time |
aggregate |
boolean | object |
Accepted and returned by models.get(); no effect on queries or fn.aggregate() |
Reference Types:
1. Basic Reference
{
key: 'category',
type: 'singleValued',
model: 'Category'
}
{
key: 'tags',
type: 'multiValued',
model: 'Tag',
separator: ','
}2. Resolved Reference
The reverse of a reference: on Category, collect every Product whose category reference points to this category. resolve.model is the model that holds the reference, resolve.reference the reference on it.
{
key: 'products',
type: 'multiValued',
model: 'Product',
resolve: {
reference: 'category',
model: 'Product'
}
}3. Derived Reference
Follows a local reference and reads a reference of the target. Here: the products of this product's category (uses the products reference on Category from above; the product itself is included).
{
key: 'similarProducts',
type: 'multiValued',
model: 'Product',
derive: {
localReference: 'category',
remoteReference: 'products'
}
}4. Grouped Reference
Collects the other items of the same model that share a value of groupBy.reference. Here: the other products in this product's category. includeSelfInGroup: true adds the item itself (default false).
{
key: 'categoryGroup',
type: 'multiValued',
model: 'Product',
groupBy: {
reference: 'category',
includeSelfInGroup: false
}
}5. Query Reference
Runs a query for every item. Values in the query can contain ${attribute} placeholders that are filled from the item's raw data, for example value: '${category}'. The query always runs against the reference's model; a modelKey inside query is ignored. query also accepts a single condition or an array of conditions, and a singleValued query reference takes the first match.
When the rows of the model that defines a query reference change, or its target model (or a model the target reaches through its references), dcupl.update() falls back to a full rebuild so the reference is recomputed; other changes stay on the partial path. See Partial Updates.
{
key: 'activeProducts',
type: 'multiValued',
model: 'Product',
query: {
modelKey: 'Product',
queries: [
{ operator: 'eq', attribute: 'status', value: 'active' }
]
}
}Property options
derive
Derives a property value from a referenced model.
Type: { localReference: string; remoteProperty: string; separator?: string }
Examples:
{
key: 'categoryName',
type: 'string',
derive: {
localReference: 'category',
remoteProperty: 'name'
}
}{
key: 'tagNames',
type: 'string',
derive: {
localReference: 'tags',
remoteProperty: 'name',
separator: ', '
}
}Over a multi-valued reference, an Array<…> property gets the list of values (separator is ignored); a string, int, float or date property gets the values joined with separator (default ', '), e.g. 'sale, new'.
expression
Defines a computed property from a template: every ${attribute} placeholder is replaced with the item's value. Expressions are text templates, not formulas: there is no arithmetic, comparison or condition, and the result is always a string (the declared type is not applied). An unknown placeholder becomes an empty string and records an UnknownExpressionVariable quality error. Compute numbers before loading the data, for example in a loader transformer.
Type: string
Examples:
{
key: 'displayName',
type: 'string',
expression: '${brand} ${name}'
}{
key: 'priceLabel',
type: 'string',
expression: '${price} EUR'
}{
key: 'label',
type: 'string',
expression: '${name} (${categoryName})' // categoryName derived from the category reference
}A placeholder can also follow a single-valued reference ('${category.name}'). It resolves, but SDK 2.0 reports it as an UnknownExpressionVariable quality error; a derived property as above avoids that. More examples: Expression properties.
quality
Defines validation rules for a property.
Type: AttributeQualityConfig
Examples:
{
key: 'name',
type: 'string',
quality: {
required: true
}
}{
key: 'email',
type: 'string',
quality: {
required: true,
validators: {
email: { value: true }
}
}
}{
key: 'age',
type: 'int',
quality: {
required: true,
validators: {
min: { value: 0 },
max: { value: 120 }
}
}
}{
key: 'zipCode',
type: 'string',
quality: {
validators: {
pattern: { value: '^[0-9]{5}$' }
}
}
}{
key: 'username',
type: 'string',
quality: {
required: true,
validators: {
unique: { value: true }
}
}
}ModelQualityConfig
Quality validation configuration for a model.
Type Definition:
type ModelQualityConfig = {
enabled?: boolean; // default: true
attributes?: Omit<AttributeQualityConfig, 'validators'>; // model-wide defaults for every attribute
};attributes sets defaults for the quality flags of all properties and references; each attribute's own quality overrides them. Validators can only be set per attribute.
Example:
{
key: 'Product',
properties: [...],
quality: {
enabled: true,
attributes: {
required: true,
nullable: false
}
}
}AttributeQualityConfig
Quality validation configuration for properties and references (on a reference only required and nullable apply).
Type Definition:
type AttributeQualityConfig = {
required?: boolean;
nullable?: boolean;
forceStrictDataType?: boolean;
validators?: AttributeValidatorConfig;
validatorHandling?: 'loose' | 'strict';
};Properties:
| Property | Type | Description |
|---|---|---|
required |
boolean |
A missing value is reported as UndefinedValue (default: true) |
nullable |
boolean |
null is allowed; with false it is reported as NullValue (default: false) |
forceStrictDataType |
boolean |
Turns off type coercion: wrong-typed values are reported as WrongDataType and dropped (default: false) |
validators |
AttributeValidatorConfig |
Validation rules |
validatorHandling |
'loose' | 'strict' |
'loose' (default) reports a failed validator and keeps the value; 'strict' also drops the value |
AttributeValidatorConfig
Validator configuration for properties.
Type Definition:
type AttributeValidatorConfig = {
email?: ValidatorConfig;
endsWith?: ValidatorConfig;
enum?: ValidatorConfig;
includes?: ValidatorConfig;
max?: ValidatorConfig;
maxLength?: ValidatorConfig;
min?: ValidatorConfig;
minLength?: ValidatorConfig;
pattern?: ValidatorConfig;
unique?: ValidatorConfig;
startsWith?: ValidatorConfig;
};Validators check string, int and float values (for Array<…> types, each element). Other property types and references are not validated, except by unique, which only detects repeated primitive values (not dates, arrays or objects). A validator runs whenever its key is present, so email: { value: false } still checks the format. A failed validator is reported as an InvalidValidator quality error.
Available Validators:
| Validator | Description | Value Type |
|---|---|---|
email |
Email format validation | boolean |
endsWith |
String ends with value | string |
enum |
Value must be in list | any[] |
includes |
String contains value | string |
max |
Maximum value | number |
maxLength |
Maximum string length | number |
min |
Minimum value | number |
minLength |
Minimum string length | number |
pattern |
Regex pattern match | string |
unique |
Unique across all items | boolean |
startsWith |
String starts with value | string |
Examples:
validators: {
email: { value: true },
minLength: { value: 5 },
maxLength: { value: 100 },
pattern: { value: '^[a-zA-Z0-9]+$' }
}
validators: {
min: { value: 0 },
max: { value: 1000 },
unique: { value: true }
}
validators: {
enum: { value: ['active', 'inactive', 'pending'] },
startsWith: { value: 'PRD-' }
}Complete examples
Product model
dcupl.models.set({
key: 'Product',
keyProperty: 'productId',
properties: [
{
key: 'name',
type: 'string',
quality: {
required: true,
validators: {
minLength: { value: 3 },
maxLength: { value: 100 },
},
},
},
{
key: 'price',
type: 'float',
fractionDigits: 2,
quality: {
required: true,
validators: {
min: { value: 0 },
},
},
},
{
key: 'description',
type: 'string',
},
{
key: 'tags',
type: 'Array<string>',
separator: ',',
},
{
key: 'createdAt',
type: 'date',
inputFormat: 'yyyy-MM-dd',
UTC: true,
},
],
references: [
{
key: 'category',
type: 'singleValued',
model: 'Category',
},
{
key: 'manufacturer',
type: 'singleValued',
model: 'Manufacturer',
},
],
quality: {
enabled: true,
},
});Model with derived properties
dcupl.models.set({
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
{
key: 'categoryName',
type: 'string',
derive: {
localReference: 'category',
remoteProperty: 'name',
},
},
{
key: 'label',
type: 'string',
expression: '${name} (${categoryName})',
},
],
references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
});Model with quality checks
dcupl.models.set({
key: 'User',
properties: [
{
key: 'username',
type: 'string',
quality: {
required: true,
validators: {
minLength: { value: 3 },
maxLength: { value: 20 },
pattern: { value: '^[a-zA-Z0-9_]+$' },
unique: { value: true },
},
},
},
{
key: 'email',
type: 'string',
quality: {
required: true,
validators: {
email: { value: true },
unique: { value: true },
},
},
},
{
key: 'age',
type: 'int',
quality: {
validators: {
min: { value: 18 },
max: { value: 120 },
},
},
},
{
key: 'status',
type: 'string',
quality: {
required: true,
validators: {
enum: { value: ['active', 'inactive', 'pending'] },
},
},
},
],
quality: {
enabled: true,
attributes: {
required: true,
nullable: false,
},
},
});Complex references
// Product model with various reference types
dcupl.models.set({
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
],
references: [
// Basic reference
{
key: 'category',
type: 'singleValued',
model: 'Category',
},
// Multi-valued reference
{
key: 'tags',
type: 'multiValued',
model: 'Tag',
separator: ',',
},
// Query reference: products in the same category (${category} is this item's category key)
{
key: 'similarProducts',
type: 'multiValued',
model: 'Product',
query: {
modelKey: 'Product',
queries: [{ operator: 'eq', attribute: 'category', value: '${category}' }],
},
},
],
});
// Category model with a resolved reference back to its products
dcupl.models.set({
key: 'Category',
properties: [{ key: 'name', type: 'string' }],
references: [
{
key: 'products',
type: 'multiValued',
model: 'Product',
resolve: {
reference: 'category',
model: 'Product',
},
},
],
});See also
- Dcupl Class API Reference
- Views: a model file can also hold a view definition, which the loader passes to
dcupl.views.set() - Data API Reference
- Models
- Data management