Expression Properties

Expression properties are template-based computed fields that combine multiple properties into dynamic, formatted strings. Perfect for display names, labels, descriptions, and any scenario where you need to compose values dynamically.

What are Expression Properties?

An expression property uses a template string with variable interpolation to create computed values:

{
  key: 'displayName',
  type: 'string',
  expression: '${firstName} ${lastName}',
}

How it works:

Input:  { firstName: 'Alice', lastName: 'Johnson' }
Output: { displayName: 'Alice Johnson' }

Key features:

  • Variable interpolation with ${variable}
  • String concatenation
  • Access to all properties in the same model
  • Access to referenced model properties
  • Automatic updates when source values change

Basic Expression

Simple Variable Interpolation

import { Dcupl } from '@dcupl/core';
import type { ModelDefinition } from '@dcupl/common';

const dcupl = new Dcupl();

const customerModel: ModelDefinition = {
  key: 'Customer',
  properties: [
    { key: 'firstName', type: 'string' },
    { key: 'lastName', type: 'string' },

    // Expression property
    {
      key: 'fullName',
      type: 'string',
      expression: '${firstName} ${lastName}',
    },
  ],
  data: [
    { key: 'c1', firstName: 'Alice', lastName: 'Johnson' },
    { key: 'c2', firstName: 'Bob', lastName: 'Smith' },
  ],
};

dcupl.models.set(customerModel);
await dcupl.init();

const customerList = dcupl.lists.create({ modelKey: 'Customer' });
const customers = customerList.catalog.query.items();

console.log(customers[0].fullName); // 'Alice Johnson'
console.log(customers[1].fullName); // 'Bob Smith'

// Filter on expression property
customerList.catalog.query.addCondition({
  attribute: 'fullName',
  operator: 'find',
  value: 'Alice',
});

String Concatenation

With Static Text

const productModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'sku', type: 'string' },
    { key: 'price', type: 'float' },

    // Product title with SKU
    {
      key: 'displayTitle',
      type: 'string',
      expression: '${name} (${sku})',
    },

    // Price label
    {
      key: 'priceLabel',
      type: 'string',
      expression: 'Price: ${price}',
    },
  ],
  data: [
    { key: 'p1', name: 'Laptop', sku: 'LPT-001', price: 999 },
    { key: 'p2', name: 'Mouse', sku: 'MSE-002', price: 29 },
  ],
};

dcupl.models.set(productModel);
await dcupl.init();

const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();

console.log(products[0].displayTitle); // 'Laptop (LPT-001)'
console.log(products[0].priceLabel); // 'Price: $999'

Multi-Line Templates

const addressModel: ModelDefinition = {
  key: 'Address',
  properties: [
    { key: 'street', type: 'string' },
    { key: 'city', type: 'string' },
    { key: 'state', type: 'string' },
    { key: 'zip', type: 'string' },

    // Full address
    {
      key: 'fullAddress',
      type: 'string',
      expression: '${street}, ${city}, ${state} ${zip}',
    },
  ],
  data: [
    {
      key: 'a1',
      street: '123 Main St',
      city: 'San Francisco',
      state: 'CA',
      zip: '94102',
    },
  ],
};

dcupl.models.set(addressModel);
await dcupl.init();

const addressList = dcupl.lists.create({ modelKey: 'Address' });
const addresses = addressList.catalog.query.items();

console.log(addresses[0].fullAddress);
// '123 Main St, San Francisco, CA 94102'

Complex Expressions

Multiple Variables

const employeeModel: ModelDefinition = {
  key: 'Employee',
  properties: [
    { key: 'firstName', type: 'string' },
    { key: 'lastName', type: 'string' },
    { key: 'title', type: 'string' },
    { key: 'department', type: 'string' },
    { key: 'email', type: 'string' },

    // Display name with title
    {
      key: 'displayName',
      type: 'string',
      expression: '${firstName} ${lastName}, ${title}',
    },

    // Email signature
    {
      key: 'emailSignature',
      type: 'string',
      expression: '${firstName} ${lastName}\n${title}\n${department}\n${email}',
    },
  ],
  data: [
    {
      key: 'e1',
      firstName: 'Alice',
      lastName: 'Johnson',
      title: 'Senior Engineer',
      department: 'Engineering',
      email: 'alice@example.com',
    },
  ],
};

dcupl.models.set(employeeModel);
await dcupl.init();

const employeeList = dcupl.lists.create({ modelKey: 'Employee' });
const employees = employeeList.catalog.query.items();

console.log(employees[0].displayName);
// 'Alice Johnson, Senior Engineer'

Using Reference Properties

Expression properties can access properties from referenced models:

const brandModel: ModelDefinition = {
  key: 'Brand',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'tagline', type: 'string' },
  ],
  data: [{ key: 'b1', name: 'Apple', tagline: 'Think Different' }],
};

const productModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'model', type: 'string' },

    // Expression using reference property
    {
      key: 'fullTitle',
      type: 'string',
      expression: '${brand.name} ${name} - ${model}',
    },
  ],
  references: [{ key: 'brand', type: 'singleValued', model: 'Brand' }],
  data: [{ key: 'p1', name: 'iPhone', model: '14 Pro', brand: 'b1' }],
};

dcupl.models.set(brandModel);
dcupl.models.set(productModel);
await dcupl.init();

const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();

console.log(products[0].fullTitle); // 'Apple iPhone - 14 Pro'

Use Cases

Use Case 1: Product Display Names

const productModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'brand', type: 'string' },
    { key: 'name', type: 'string' },
    { key: 'variant', type: 'string' },
    { key: 'color', type: 'string' },
    { key: 'size', type: 'string' },

    // Product display title
    {
      key: 'displayTitle',
      type: 'string',
      expression: '${brand} ${name} - ${variant} (${color}, ${size})',
    },
  ],
  data: [
    {
      key: 'p1',
      brand: 'Nike',
      name: 'Air Max',
      variant: '270',
      color: 'Black',
      size: '10',
    },
  ],
};

dcupl.models.set(productModel);
await dcupl.init();

const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();

console.log(products[0].displayTitle);
// 'Nike Air Max - 270 (Black, 10)'

Use Case 2: Contact Information

const contactModel: ModelDefinition = {
  key: 'Contact',
  properties: [
    { key: 'firstName', type: 'string' },
    { key: 'lastName', type: 'string' },
    { key: 'company', type: 'string' },
    { key: 'phone', type: 'string' },
    { key: 'email', type: 'string' },

    // Full name
    {
      key: 'fullName',
      type: 'string',
      expression: '${firstName} ${lastName}',
    },

    // Contact card
    {
      key: 'contactCard',
      type: 'string',
      expression: '${fullName} | ${company} | ${phone} | ${email}',
    },
  ],
  data: [
    {
      key: 'c1',
      firstName: 'Alice',
      lastName: 'Johnson',
      company: 'Acme Corp',
      phone: '555-1234',
      email: 'alice@acme.com',
    },
  ],
};

dcupl.models.set(contactModel);
await dcupl.init();

const contactList = dcupl.lists.create({ modelKey: 'Contact' });
const contacts = contactList.catalog.query.items();

console.log(contacts[0].contactCard);
// 'Alice Johnson | Acme Corp | 555-1234 | alice@acme.com'

Use Case 3: Order Summary

const orderModel: ModelDefinition = {
  key: 'Order',
  properties: [
    { key: 'orderNumber', type: 'string' },
    { key: 'customerName', type: 'string' },
    { key: 'itemCount', type: 'int' },
    { key: 'total', type: 'float' },
    { key: 'status', type: 'string' },

    // Order summary line
    {
      key: 'summary',
      type: 'string',
      expression:
        'Order #${orderNumber} - ${customerName} (${itemCount} items, ${total}) - ${status}',
    },
  ],
  data: [
    {
      key: 'o1',
      orderNumber: 'ORD-1001',
      customerName: 'Alice Johnson',
      itemCount: 3,
      total: 299.99,
      status: 'Shipped',
    },
  ],
};

dcupl.models.set(orderModel);
await dcupl.init();

const orderList = dcupl.lists.create({ modelKey: 'Order' });
const orders = orderList.catalog.query.items();

console.log(orders[0].summary);
// 'Order #ORD-1001 - Alice Johnson (3 items, $299.99) - Shipped'

Use Case 4: File Paths

const fileModel: ModelDefinition = {
  key: 'File',
  properties: [
    { key: 'directory', type: 'string' },
    { key: 'filename', type: 'string' },
    { key: 'extension', type: 'string' },

    // Full file path
    {
      key: 'fullPath',
      type: 'string',
      expression: '${directory}/${filename}.${extension}',
    },
  ],
  data: [{ key: 'f1', directory: '/documents/reports', filename: 'sales-2025', extension: 'pdf' }],
};

dcupl.models.set(fileModel);
await dcupl.init();

const fileList = dcupl.lists.create({ modelKey: 'File' });
const files = fileList.catalog.query.items();

console.log(files[0].fullPath);
// '/documents/reports/sales-2025.pdf'

Use Case 5: URL Slugs

const articleModel: ModelDefinition = {
  key: 'Article',
  properties: [
    { key: 'year', type: 'int' },
    { key: 'month', type: 'int' },
    { key: 'slug', type: 'string' },

    // URL path
    {
      key: 'urlPath',
      type: 'string',
      expression: '/blog/${year}/${month}/${slug}',
    },
  ],
  data: [{ key: 'a1', year: 2025, month: 12, slug: 'getting-started-with-dcupl' }],
};

dcupl.models.set(articleModel);
await dcupl.init();

const articleList = dcupl.lists.create({ modelKey: 'Article' });
const articles = articleList.catalog.query.items();

console.log(articles[0].urlPath);
// '/blog/2025/12/getting-started-with-dcupl'

Use Case 6: Badge Labels

const userModel: ModelDefinition = {
  key: 'User',
  properties: [
    { key: 'username', type: 'string' },
    { key: 'level', type: 'int' },
    { key: 'rank', type: 'string' },
    { key: 'points', type: 'int' },

    // User badge
    {
      key: 'badge',
      type: 'string',
      expression: '${username} - Level ${level} ${rank} (${points} pts)',
    },
  ],
  data: [{ key: 'u1', username: 'alice_dev', level: 42, rank: 'Expert', points: 8500 }],
};

dcupl.models.set(userModel);
await dcupl.init();

const userList = dcupl.lists.create({ modelKey: 'User' });
const users = userList.catalog.query.items();

console.log(users[0].badge);
// 'alice_dev - Level 42 Expert (8500 pts)'

Use Case 7: Event Descriptions

const eventModel: ModelDefinition = {
  key: 'Event',
  properties: [
    { key: 'title', type: 'string' },
    { key: 'date', type: 'string' },
    { key: 'location', type: 'string' },
    { key: 'attendees', type: 'int' },

    // Event description
    {
      key: 'description',
      type: 'string',
      expression: '${title} on ${date} at ${location} (${attendees} attendees)',
    },
  ],
  data: [
    {
      key: 'e1',
      title: 'Tech Conference 2025',
      date: '2025-06-15',
      location: 'San Francisco',
      attendees: 500,
    },
  ],
};

dcupl.models.set(eventModel);
await dcupl.init();

const eventList = dcupl.lists.create({ modelKey: 'Event' });
const events = eventList.catalog.query.items();

console.log(events[0].description);
// 'Tech Conference 2025 on 2025-06-15 at San Francisco (500 attendees)'

Use Case 8: Inventory Labels

const inventoryModel: ModelDefinition = {
  key: 'Inventory',
  properties: [
    { key: 'productName', type: 'string' },
    { key: 'warehouse', type: 'string' },
    { key: 'location', type: 'string' },
    { key: 'quantity', type: 'int' },

    // Inventory label
    {
      key: 'label',
      type: 'string',
      expression: '${productName} | WH: ${warehouse} | LOC: ${location} | QTY: ${quantity}',
    },
  ],
  data: [
    {
      key: 'i1',
      productName: 'Widget Pro',
      warehouse: 'WH-01',
      location: 'A-12-3',
      quantity: 150,
    },
  ],
};

dcupl.models.set(inventoryModel);
await dcupl.init();

const inventoryList = dcupl.lists.create({ modelKey: 'Inventory' });
const inventory = inventoryList.catalog.query.items();

console.log(inventory[0].label);
// 'Widget Pro | WH: WH-01 | LOC: A-12-3 | QTY: 150'

Advanced Examples

Example 9: Currency Formatting

const priceModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
    { key: 'currency', type: 'string' },

    // Price with currency
    {
      key: 'formattedPrice',
      type: 'string',
      expression: '${price} ${currency}',
    },
  ],
  data: [
    { key: 'p1', name: 'Laptop', price: 999.99, currency: 'USD' },
    { key: 'p2', name: 'Mouse', price: 29.99, currency: 'EUR' },
  ],
};

dcupl.models.set(priceModel);
await dcupl.init();

const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();

console.log(products[0].formattedPrice); // '999.99 USD'
console.log(products[1].formattedPrice); // '29.99 EUR'

Example 10: Coordinates

const locationModel: ModelDefinition = {
  key: 'Location',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'latitude', type: 'float' },
    { key: 'longitude', type: 'float' },

    // Coordinates string
    {
      key: 'coordinates',
      type: 'string',
      expression: '${latitude}, ${longitude}',
    },

    // Google Maps URL
    {
      key: 'mapsUrl',
      type: 'string',
      expression: 'https://maps.google.com/?q=${latitude},${longitude}',
    },
  ],
  data: [{ key: 'l1', name: 'Office', latitude: 37.7749, longitude: -122.4194 }],
};

dcupl.models.set(locationModel);
await dcupl.init();

const locationList = dcupl.lists.create({ modelKey: 'Location' });
const locations = locationList.catalog.query.items();

console.log(locations[0].coordinates); // '37.7749, -122.4194'
console.log(locations[0].mapsUrl);
// 'https://maps.google.com/?q=37.7749,-122.4194'

Example 11: Version Numbers

const softwareModel: ModelDefinition = {
  key: 'Software',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'major', type: 'int' },
    { key: 'minor', type: 'int' },
    { key: 'patch', type: 'int' },

    // Semantic version
    {
      key: 'version',
      type: 'string',
      expression: '${major}.${minor}.${patch}',
    },

    // Full software name
    {
      key: 'fullName',
      type: 'string',
      expression: '${name} v${version}',
    },
  ],
  data: [{ key: 's1', name: 'dcupl', major: 2, minor: 5, patch: 3 }],
};

dcupl.models.set(softwareModel);
await dcupl.init();

const softwareList = dcupl.lists.create({ modelKey: 'Software' });
const software = softwareList.catalog.query.items();

console.log(software[0].version); // '2.5.3'
console.log(software[0].fullName); // 'dcupl v2.5.3'

Example 12: Date Ranges

const projectModel: ModelDefinition = {
  key: 'Project',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'startDate', type: 'string' },
    { key: 'endDate', type: 'string' },

    // Date range
    {
      key: 'dateRange',
      type: 'string',
      expression: '${startDate} to ${endDate}',
    },
  ],
  data: [{ key: 'p1', name: 'Website Redesign', startDate: '2025-01-15', endDate: '2025-03-30' }],
};

dcupl.models.set(projectModel);
await dcupl.init();

const projectList = dcupl.lists.create({ modelKey: 'Project' });
const projects = projectList.catalog.query.items();

console.log(projects[0].dateRange); // '2025-01-15 to 2025-03-30'

Example 13: Dimensions

const packageModel: ModelDefinition = {
  key: 'Package',
  properties: [
    { key: 'length', type: 'float' },
    { key: 'width', type: 'float' },
    { key: 'height', type: 'float' },
    { key: 'unit', type: 'string' },

    // Dimensions string
    {
      key: 'dimensions',
      type: 'string',
      expression: '${length} x ${width} x ${height} ${unit}',
    },
  ],
  data: [{ key: 'pkg1', length: 12.5, width: 8.0, height: 4.5, unit: 'inches' }],
};

dcupl.models.set(packageModel);
await dcupl.init();

const packageList = dcupl.lists.create({ modelKey: 'Package' });
const packages = packageList.catalog.query.items();

console.log(packages[0].dimensions); // '12.5 x 8.0 x 4.5 inches'

Example 14: Social Media Handles

const userModel: ModelDefinition = {
  key: 'User',
  properties: [
    { key: 'username', type: 'string' },
    { key: 'twitter', type: 'string' },
    { key: 'github', type: 'string' },

    // Twitter URL
    {
      key: 'twitterUrl',
      type: 'string',
      expression: 'https://twitter.com/${twitter}',
    },

    // GitHub URL
    {
      key: 'githubUrl',
      type: 'string',
      expression: 'https://github.com/${github}',
    },

    // Social links
    {
      key: 'socialLinks',
      type: 'string',
      expression: 'Twitter: @${twitter} | GitHub: @${github}',
    },
  ],
  data: [{ key: 'u1', username: 'alice', twitter: 'alice_dev', github: 'alicedev' }],
};

dcupl.models.set(userModel);
await dcupl.init();

const userList = dcupl.lists.create({ modelKey: 'User' });
const users = userList.catalog.query.items();

console.log(users[0].twitterUrl); // 'https://twitter.com/alice_dev'
console.log(users[0].githubUrl); // 'https://github.com/alicedev'
console.log(users[0].socialLinks); // 'Twitter: @alice_dev | GitHub: @alicedev'

Example 15: Recipe Titles

const recipeModel: ModelDefinition = {
  key: 'Recipe',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'cuisine', type: 'string' },
    { key: 'prepTime', type: 'int' },
    { key: 'servings', type: 'int' },

    // Recipe display title
    {
      key: 'displayTitle',
      type: 'string',
      expression: '${name} (${cuisine}) - ${prepTime} min, serves ${servings}',
    },
  ],
  data: [{ key: 'r1', name: 'Pasta Carbonara', cuisine: 'Italian', prepTime: 30, servings: 4 }],
};

dcupl.models.set(recipeModel);
await dcupl.init();

const recipeList = dcupl.lists.create({ modelKey: 'Recipe' });
const recipes = recipeList.catalog.query.items();

console.log(recipes[0].displayTitle);
// 'Pasta Carbonara (Italian) - 30 min, serves 4'

Example 16: Academic Titles

const facultyModel: ModelDefinition = {
  key: 'Faculty',
  properties: [
    { key: 'title', type: 'string' },
    { key: 'firstName', type: 'string' },
    { key: 'lastName', type: 'string' },
    { key: 'degree', type: 'string' },
    { key: 'department', type: 'string' },

    // Formal name
    {
      key: 'formalName',
      type: 'string',
      expression: '${title} ${firstName} ${lastName}, ${degree}',
    },

    // Full title
    {
      key: 'fullTitle',
      type: 'string',
      expression: '${formalName}, ${department}',
    },
  ],
  data: [
    {
      key: 'f1',
      title: 'Dr.',
      firstName: 'Alice',
      lastName: 'Johnson',
      degree: 'Ph.D.',
      department: 'Computer Science',
    },
  ],
};

dcupl.models.set(facultyModel);
await dcupl.init();

const facultyList = dcupl.lists.create({ modelKey: 'Faculty' });
const faculty = facultyList.catalog.query.items();

console.log(faculty[0].formalName); // 'Dr. Alice Johnson, Ph.D.'
console.log(faculty[0].fullTitle);
// 'Dr. Alice Johnson, Ph.D., Computer Science'

Example 17: SKU Generation

const productModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'categoryCode', type: 'string' },
    { key: 'brandCode', type: 'string' },
    { key: 'productNumber', type: 'string' },
    { key: 'colorCode', type: 'string' },

    // Auto-generated SKU
    {
      key: 'sku',
      type: 'string',
      index: true,
      expression: '${categoryCode}-${brandCode}-${productNumber}-${colorCode}',
    },
  ],
  data: [
    { key: 'p1', categoryCode: 'ELC', brandCode: 'APL', productNumber: '001', colorCode: 'BLK' },
  ],
};

dcupl.models.set(productModel);
await dcupl.init();

const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();

console.log(products[0].sku); // 'ELC-APL-001-BLK'

Example 18: Email Subjects

const emailModel: ModelDefinition = {
  key: 'Email',
  properties: [
    { key: 'recipientName', type: 'string' },
    { key: 'orderNumber', type: 'string' },
    { key: 'status', type: 'string' },

    // Email subject line
    {
      key: 'subject',
      type: 'string',
      expression: 'Order ${orderNumber} ${status} - ${recipientName}',
    },
  ],
  data: [{ key: 'e1', recipientName: 'Alice Johnson', orderNumber: 'ORD-1001', status: 'Shipped' }],
};

dcupl.models.set(emailModel);
await dcupl.init();

const emailList = dcupl.lists.create({ modelKey: 'Email' });
const emails = emailList.catalog.query.items();

console.log(emails[0].subject);
// 'Order ORD-1001 Shipped - Alice Johnson'

Example 19: Citation Format

const publicationModel: ModelDefinition = {
  key: 'Publication',
  properties: [
    { key: 'author', type: 'string' },
    { key: 'year', type: 'int' },
    { key: 'title', type: 'string' },
    { key: 'journal', type: 'string' },

    // APA citation
    {
      key: 'citation',
      type: 'string',
      expression: '${author} (${year}). ${title}. ${journal}.',
    },
  ],
  data: [
    {
      key: 'pub1',
      author: 'Smith, J.',
      year: 2025,
      title: 'Advances in Data Processing',
      journal: 'Journal of Computer Science',
    },
  ],
};

dcupl.models.set(publicationModel);
await dcupl.init();

const publicationList = dcupl.lists.create({ modelKey: 'Publication' });
const publications = publicationList.catalog.query.items();

console.log(publications[0].citation);
// 'Smith, J. (2025). Advances in Data Processing. Journal of Computer Science.'

Example 20: Vehicle Identification

const vehicleModel: ModelDefinition = {
  key: 'Vehicle',
  properties: [
    { key: 'year', type: 'int' },
    { key: 'make', type: 'string' },
    { key: 'model', type: 'string' },
    { key: 'trim', type: 'string' },
    { key: 'color', type: 'string' },

    // Vehicle description
    {
      key: 'description',
      type: 'string',
      expression: '${year} ${make} ${model} ${trim} - ${color}',
    },
  ],
  data: [
    { key: 'v1', year: 2025, make: 'Tesla', model: 'Model 3', trim: 'Long Range', color: 'Blue' },
  ],
};

dcupl.models.set(vehicleModel);
await dcupl.init();

const vehicleList = dcupl.lists.create({ modelKey: 'Vehicle' });
const vehicles = vehicleList.catalog.query.items();

console.log(vehicles[0].description);
// '2025 Tesla Model 3 Long Range - Blue'

Example 21: Medical Records

const patientModel: ModelDefinition = {
  key: 'Patient',
  properties: [
    { key: 'lastName', type: 'string' },
    { key: 'firstName', type: 'string' },
    { key: 'dob', type: 'string' },
    { key: 'mrn', type: 'string' },

    // Patient identifier
    {
      key: 'patientId',
      type: 'string',
      expression: '${lastName}, ${firstName} (DOB: ${dob}, MRN: ${mrn})',
    },
  ],
  data: [
    { key: 'pt1', lastName: 'Johnson', firstName: 'Alice', dob: '1985-06-15', mrn: 'MRN-12345' },
  ],
};

dcupl.models.set(patientModel);
await dcupl.init();

const patientList = dcupl.lists.create({ modelKey: 'Patient' });
const patients = patientList.catalog.query.items();

console.log(patients[0].patientId);
// 'Johnson, Alice (DOB: 1985-06-15, MRN: MRN-12345)'

Example 22: Real Estate Listings

const propertyModel: ModelDefinition = {
  key: 'Property',
  properties: [
    { key: 'address', type: 'string' },
    { key: 'beds', type: 'int' },
    { key: 'baths', type: 'float' },
    { key: 'sqft', type: 'int' },
    { key: 'price', type: 'int' },

    // Property listing
    {
      key: 'listing',
      type: 'string',
      expression: '${address} - ${beds}bd/${baths}ba, ${sqft} sqft - ${price}',
    },
  ],
  data: [{ key: 'prop1', address: '123 Main St', beds: 3, baths: 2.5, sqft: 2200, price: 750000 }],
};

dcupl.models.set(propertyModel);
await dcupl.init();

const propertyList = dcupl.lists.create({ modelKey: 'Property' });
const properties = propertyList.catalog.query.items();

console.log(properties[0].listing);
// '123 Main St - 3bd/2.5ba, 2200 sqft - $750000'

Example 23: Course Titles

const courseModel: ModelDefinition = {
  key: 'Course',
  properties: [
    { key: 'code', type: 'string' },
    { key: 'number', type: 'int' },
    { key: 'title', type: 'string' },
    { key: 'credits', type: 'int' },

    // Course identifier
    {
      key: 'courseId',
      type: 'string',
      expression: '${code} ${number}',
    },

    // Full course name
    {
      key: 'fullName',
      type: 'string',
      expression: '${courseId}: ${title} (${credits} credits)',
    },
  ],
  data: [{ key: 'c1', code: 'CS', number: 101, title: 'Introduction to Programming', credits: 4 }],
};

dcupl.models.set(courseModel);
await dcupl.init();

const courseList = dcupl.lists.create({ modelKey: 'Course' });
const courses = courseList.catalog.query.items();

console.log(courses[0].courseId); // 'CS 101'
console.log(courses[0].fullName);
// 'CS 101: Introduction to Programming (4 credits)'

Example 24: Flight Information

const flightModel: ModelDefinition = {
  key: 'Flight',
  properties: [
    { key: 'airline', type: 'string' },
    { key: 'flightNumber', type: 'string' },
    { key: 'origin', type: 'string' },
    { key: 'destination', type: 'string' },
    { key: 'departureTime', type: 'string' },

    // Flight code
    {
      key: 'flightCode',
      type: 'string',
      expression: '${airline} ${flightNumber}',
    },

    // Flight info
    {
      key: 'flightInfo',
      type: 'string',
      expression: '${flightCode}: ${origin} -> ${destination} (${departureTime})',
    },
  ],
  data: [
    {
      key: 'f1',
      airline: 'UA',
      flightNumber: '123',
      origin: 'SFO',
      destination: 'JFK',
      departureTime: '10:30 AM',
    },
  ],
};

dcupl.models.set(flightModel);
await dcupl.init();

const flightList = dcupl.lists.create({ modelKey: 'Flight' });
const flights = flightList.catalog.query.items();

console.log(flights[0].flightInfo);
// 'UA 123: SFO → JFK (10:30 AM)'

Example 25: Issue Tracking

const issueModel: ModelDefinition = {
  key: 'Issue',
  properties: [
    { key: 'projectKey', type: 'string' },
    { key: 'issueNumber', type: 'int' },
    { key: 'title', type: 'string' },
    { key: 'priority', type: 'string' },
    { key: 'assignee', type: 'string' },

    // Issue key
    {
      key: 'issueKey',
      type: 'string',
      index: true,
      expression: '${projectKey}-${issueNumber}',
    },

    // Issue summary
    {
      key: 'summary',
      type: 'string',
      expression: '[${issueKey}] ${title} (${priority}) - ${assignee}',
    },
  ],
  data: [
    {
      key: 'i1',
      projectKey: 'PROJ',
      issueNumber: 123,
      title: 'Fix login bug',
      priority: 'High',
      assignee: 'Alice',
    },
  ],
};

dcupl.models.set(issueModel);
await dcupl.init();

const issueList = dcupl.lists.create({ modelKey: 'Issue' });
const issues = issueList.catalog.query.items();

console.log(issues[0].issueKey); // 'PROJ-123'
console.log(issues[0].summary);
// '[PROJ-123] Fix login bug (High) - Alice'

Best Practices

1. Enable Filtering on Expression Properties

// ✅ Good: Make expression properties filterable
{
  key: 'fullName',
  type: 'string',
  expression: '${firstName} ${lastName}',
}

// ❌ Bad: Missing filter flag
{
  key: 'fullName',
  type: 'string',
  expression: '${firstName} ${lastName}',
}

2. Use Clear Variable Names

// ✅ Good: Clear template
expression: '${firstName} ${lastName}';

// ❌ Bad: Unclear variables
expression: '${a} ${b}';

3. Keep Templates Simple

// ✅ Good: Simple, readable
expression: '${brand} ${name} - ${model}';

// ⚠️ OK but complex
expression: '${category.type} > ${category.name} > ${brand.name} ${name} (${sku})';

4. Consider Performance

Expression properties are computed at init and during updates. Complex expressions with many variables have minimal performance impact.

5. Use for Display, Not Logic

// ✅ Good: Display purposes
{
  key: 'displayName',
  type: 'string',
  expression: '${firstName} ${lastName}',
}

// ❌ Bad: Don't use for calculations
{
  key: 'total',
  type: 'string',
  expression: '${price} * ${quantity}',  // Won't calculate!
}

Troubleshooting

Expression Not Evaluating

Problem: Expression shows literal template

console.log(product.displayName); // '${brand} ${name}'

Solution: Call dcupl.init():

await dcupl.init(); // Must init before accessing expressions

Undefined in Expression

Problem: Expression shows undefined values

console.log(product.fullTitle); // 'Apple undefined'

Solution: Check source properties exist:

console.log(product.name); // Verify source property

Reference Property Not Accessible

Problem: Can't access reference properties

expression: '${brand.name} ${name}'; // brand.name is undefined

Solution: Ensure reference is resolved:

await dcupl.init(); // Resolves references first

Expression Too Complex

Problem: Very long expression is hard to maintain

Solution: Break into multiple expressions:

// Instead of one huge expression
{
  key: 'description',
  expression: '${a} ${b} ${c} ${d} ${e} ${f} ${g}',
}

// Create intermediate expressions
{
  key: 'partOne',
  expression: '${a} ${b} ${c}',
},
{
  key: 'partTwo',
  expression: '${d} ${e}',
},
{
  key: 'description',
  expression: '${partOne} - ${partTwo} ${f} ${g}',
}

What's Next?