Introduction

The template engine is a core feature of Compozy that allows you to:

  • Reference data from various workflow components
  • Perform dynamic calculations and transformations
  • Access environment variables and configuration
  • Handle conditional logic inline

Compozy uses Jinja2-style syntax combined with Deno for JavaScript evaluation, providing:

  • Simple expressions using {{ ... }} for single-line JavaScript
  • Complex logic using $exec: | for multiline JavaScript code

Since we use Deno runtime, all JavaScript code in templates follows modern ECMAScript standards and has access to Deno’s built-in security features.

Template Syntax

Simple Expressions

For basic operations, use the {{ ... }} syntax. These expressions should be concise and focus on a single operation:

# Basic variable access
name: "{{ user.name }}"

# Simple calculations
total: "{{ price * quantity }}"

# Conditional values
status: "{{ isActive ? 'running' : 'stopped' }}"

Multiline Code

For complex operations that require multiple lines or intermediate variables, use the $exec: | syntax:

description:
  $exec: |
    const { title, author, date } = metadata;
    const formattedDate = new Date(date).toLocaleDateString();
    return `${title} by ${author} (${formattedDate})`;

Working with Objects and Arrays

# Property access
value: "{{ object.nested.property }}"

# Optional chaining
value: "{{ object?.nested?.property }}"

# Default values
value: "{{ object.property ?? 'default' }}"

Built-in Functions

Compozy includes several popular libraries for common operations. Here are some examples:

# Lodash array utilities
chunks: "{{ _.chunk(array, 2) }}"

# String manipulation
slug: "{{ v.slugify(title) }}"

For a complete reference of available functions, see Template Functions.

Custom Dependencies

You can extend the template engine with additional libraries. All custom dependencies are automatically namespaced under the $ object:

---
name: "My Document"
template-dependencies:
  - package: "npm:mathjs@11.8.0"
    name: math
---

# Access custom dependencies through the $ object
result: "{{ $.math.evaluate('2x + 3y', { x: 4, y: 2 }) }}"

Learn more about adding custom dependencies in Custom Dependencies.

Best Practices

1

Keep Expressions Simple

Use simple expressions for basic operations:

status: "{{ isActive && isValid }}"

Move complex logic to multiline blocks:

status:
  $exec: |
    if (!isActive) return 'inactive';
    if (!isValid) return 'invalid';
    return 'ready';
2

Use Optional Chaining

Always use optional chaining for nested properties:

value: "{{ response?.data?.items ?? [] }}"
3

Handle Errors

Implement proper error handling in complex operations:

result:
  $exec: |
    try {
      return processData(input);
    } catch (error) {
      return { error: error.message };
    }
4

Type Safety

Use explicit type conversions:

count: "{{ Number(value) }}"
flag: "{{ Boolean(value) }}"

Next Steps

References