Skip to main content
Preview
Preview Feature — This feature is currently in preview and under active development. APIs and functionality may change. We recommend testing thoroughly before using in production.

Nesting Operators

Operators in Ductape can be nested to perform complex data transformations in a single expression. This enables powerful data manipulation while keeping your workflow definitions declarative and inspectable.

Why Nest Operators?

Nesting allows you to:

  • Chain transformations - Apply multiple operations in sequence
  • Build complex logic - Combine simple operators to solve complex problems
  • Keep workflows concise - Avoid intermediate steps and temporary values
  • Maintain readability - Express intent clearly with composed operations

How Nesting Works

Operators are evaluated from the inside out, just like function composition:

$Uppercase($Trim($Input{name}))

Execution order:

  1. Get value from $Input{name}" john "
  2. Apply $Trim()"john"
  3. Apply $Uppercase()"JOHN"

Real-World Examples with Data References

Example 1: User Profile Formatting

// Feature input: { firstName: "  Jane  ", lastName: "Doe", email: "JANE@EXAMPLE.COM  " }

output: {
// Trim and concatenate names
fullName: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))',
// Result: "Jane Doe"

// Normalize and format email
email: '$Lowercase($Trim($Input{email}))',
// Result: "jane@example.com"

// Create initials from names
initials: '$Concat([$Uppercase($Pick($Input{firstName}, 0)), $Uppercase($Pick($Input{lastName}, 0))], "")',
// Result: "JD"
}

Example 2: Processing API Responses

// Assuming create-user action returns: { id: "usr_123abc456def", created_at: "2024-07-15T10:30:00Z" }

output: {
// Extract short user ID (first 8 chars)
shortId: '$Substring($Sequence{main}{create-user}{id}, 4, 12)',
// Result: "123abc45"

// Format creation date
createdDate: '$Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")',
// Result: "15/07/2024"

// Combine formatted data
displayText: '$Concat(["User ", $Substring($Sequence{main}{create-user}{id}, 4, 12), " created on ", $Dateformat($Sequence{main}{create-user}{created_at}, "DD/MM/YYYY")], "")',
// Result: "User 123abc45 created on 15/07/2024"
}

Example 3: Payment Processing

// Input: { baseAmount: 100, taxRate: 0.2, discountCode: "SAVE20" }
// Sequence result: { discount: { value: 20 } }

output: {
// Calculate tax amount
taxAmount: '$Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value})',
// Step 1: $Sequence{main}{calculate-discount}{value} → 20
// Step 2: $Subtract(100, 20) → 80

// Format final price with currency
displayPrice: '$Concat(["$", $Add($Subtract($Input{baseAmount}, $Sequence{main}{calculate-discount}{value}), 0.00)], "")',
// Result: "$80.00"
}

Example 4: Array Filtering and Extraction

// Sequence result: { orders: [
// { id: 1, status: 'pending', amount: 50 },
// { id: 2, status: 'completed', amount: 100 },
// { id: 3, status: 'completed', amount: 75 }
// ]}

output: {
// Filter and get first completed order
firstCompleted: '$Find($Sequence{main}{fetch-orders}{orders}, "==", "completed")',
// Result: { id: 2, status: 'completed', amount: 100 }

// Get all completed orders and join with pending
allOrders: '$Join([$Filter($Sequence{main}{fetch-orders}{orders}, "==", "completed"), $Sequence{main}{fetch-pending}{orders}])',
// Combines filtered completed orders with pending orders
}

Example 5: Deep Nesting with Multiple Data Sources

// Input: { userId: "usr_123", orderDate: "2024-07-15" }
// Session: { user: { name: "John Doe", email: "john@example.com" } }
// Sequence: { create-order: { id: "ord_456", total: 250.50 } }
// Constant: { config: { currency: "USD" } }

output: {
// Complex formatted message
orderSummary: '$Concat([
"Order ",
$Substring($Sequence{main}{create-order}{id}, 4),
" for ",
$Uppercase($Pick($Session{user}{name}, 0)),
$Substring($Session{user}{name}, 1),
" on ",
$Dateformat($Input{orderDate}, "MMM DD, YYYY"),
" - Total: ",
$Constant{config}{currency},
" ",
$Sequence{main}{create-order}{total}
], "")',
// Result: "Order 456 for John Doe on Jul 15, 2024 - Total: USD 250.50"
}

Basic Nesting Patterns

Examples of Nested Operators:

  1. Nested $Add with $Substring and $Pick:
$Add( $Pick('12345', 0), $Substring('98765', 2, 4) )
  • $Pick('12345', 0)'1'1
  • $Substring('98765', 2, 4)'76'76
  • $Add(1, 76)77
  1. Nested $Concat with $Trim and $Split:
$Concat([$Trim('  hello  '), $Pick($Split('a,b,c', ','), 1)], '-')
  • $Trim(' hello ')'hello'
  • $Split('a,b,c', ',')['a', 'b', 'c']
  • $Pick(['a', 'b', 'c'], 1)'b'
  • $Concat(['hello', 'b'], '-')'hello-b'
  1. Nested $Subtract with $Add and $Pick:
$Subtract( $Add(2, 3), $Pick('678', 1) )
  • $Add(2, 3)5
  • $Pick('678', 1)'7'7
  • $Subtract(5, 7)-2
  1. Complex Nesting with $Join, $Split, and $Trim:
$Join([$Trim(' John '), $Pick($Split('Jane,Doe', ','), 1)], ' & ')
  • $Trim(' John ')'John'
  • $Split('Jane,Doe', ',')['Jane', 'Doe']
  • $Pick(['Jane', 'Doe'], 1)'Doe'
  • $Join(['John', 'Doe'], ' & ')'John & Doe'
  1. Deep Nesting Example:
$Concat([
$Add( $Pick('12', 0), 5 ),
$Substring('abcdef', 2, 5),
$Trim(' xyz ')
], '_')
  • $Pick('12', 0)'1'1
  • $Add(1, 5)6
  • $Substring('abcdef', 2, 5)'cde'
  • $Trim(' xyz ')'xyz'
  • $Concat(['6', 'cde', 'xyz'], '_')'6_cde_xyz'

Best Practices for Nesting

1. Keep It Readable

While deep nesting is powerful, prioritize clarity:

Good - Clear intent:

fullName: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))'

Harder to read:

result: '$Uppercase($Trim($Concat([$Substring($Input{first}, 0, 1), $Lowercase($Pick($Split($Input{last}, " "), 0))], "-")))'

2. Work from Inside Out

When building complex nesting, start with the innermost operation:

// Step 1: Get the data
$Input{email}

// Step 2: Trim whitespace
$Trim($Input{email})

// Step 3: Convert to lowercase
$Lowercase($Trim($Input{email}))

// Step 4: Extract domain
$Pick($Split($Lowercase($Trim($Input{email})), "@"), 1)

3. Validate Your Data Sources

Ensure the data you're referencing exists and matches the expected type:

// Make sure the event result has this field before extracting
userId: '$Substring($Sequence{main}{create-user}{id}, 0, 8)'

// Verify array results before filtering
activeUsers: '$Filter($Sequence{main}{fetch-users}{users}, "==", "active")'

4. Use Comments in Complex Workflows

Document your transformations, especially nested operators:

await ductape.features.create({
tag: 'format-order',
output: {
// Extract order ID prefix (first 8 chars after "ord_")
shortOrderId: '$Substring($Sequence{main}{create-order}{id}, 4, 12)',

// Format: "Order #123 by John D. on Jul 15"
displaySummary: '$Concat([
"Order #",
$Substring($Sequence{main}{create-order}{id}, 4, 12),
" by ",
$Trim($Session{user}{name}),
" on ",
$Dateformat($Input{orderDate}, "MMM DD")
], "")',
}
});

Common Nesting Patterns

Pattern 1: Clean User Input

// Normalize and validate user input
{
email: '$Lowercase($Trim($Input{email}))',
name: '$Trim($Concat([$Input{firstName}, $Input{lastName}], " "))',
phone: '$Replace($Trim($Input{phone}), "-", "")',
}

Pattern 2: Format API Responses

// Transform third-party API data
{
userId: '$Substring($Response{id}, 0, 8)',
createdDate: '$Dateformat($Response{created_at}, "DD/MM/YYYY")',
displayName: '$Uppercase($Pick($Response{name}, 0))$Substring($Response{name}, 1)',
}

Pattern 3: Combine Multiple Sources

// Merge data from different sources
{
userInfo: '$Concat([
$Session{user}{name},
" (",
$Lowercase($Input{email}),
") - ID: ",
$Substring($Sequence{main}{create-user}{id}, 4)
], "")',
}

Pattern 4: Conditional Extraction

// Extract specific data based on conditions
{
completedOrders: '$Filter($Sequence{main}{fetch-orders}{data}, "==", "completed")',
firstPending: '$Find($Sequence{main}{fetch-orders}{data}, "==", "pending")',
highValueOrders: '$Filter($Sequence{main}{fetch-orders}{data}, ">", 1000)',
}

Tips for Success

Test incrementally - Build complex expressions step by step, testing each level

Use consistent spacing - Format nested operators for readability

Document complex logic - Add comments explaining the transformation intent

Validate data types - Ensure operations match the data type (strings, numbers, arrays)

Consider sequence order - Reference events that have already executed in the workflow

Keep transformations pure - Operators should not have side effects

Debugging Nested Operators

When a nested operator doesn't produce expected results:

  1. Check the innermost operation first - Verify each data reference resolves correctly
  2. Inspect intermediate values - Use simpler expressions to test each step
  3. Verify data types - Ensure operators receive the correct input types
  4. Review execution order - Confirm referenced events have completed
  5. Check for null/undefined - Ensure all referenced fields exist

See Also