Skip to main content
Take your variable mapping skills to the next level with advanced patterns, optimization strategies, and power user techniques.
This guide assumes you’re comfortable with basic syntax, filters, and the pipe operator. If you’re new to variable mapping, start with Basic Syntax.

Combining Multiple Data Sources

Reference and combine data from triggers, multiple nodes, and nested structures:
Mapping
{
  "enrichedOrder": {
    "orderId": "$.trigger.order_id",
    "customer": {
      "id": "$.trigger.customer_id",
      "name": "$.fetch_customer.name",
      "email": "$.fetch_customer.email",
      "tier": "$.fetch_customer.tier"
    },
    "orderDetails": {
      "items": "$.fetch_order.items",
      "subtotal": "$.fetch_order.subtotal",
      "tax": "$.calculate_tax.amount",
      "total": "$.calculate_total.final_amount"
    },
    "shipping": {
      "address": "$.fetch_customer.shipping_address",
      "method": "$.fetch_order.shipping_method",
      "cost": "$.calculate_shipping.cost",
      "estimatedDays": "$.calculate_shipping.estimated_days"
    }
  }
}
Combine trigger data with multiple node outputs into a unified structure.

Nested Filter Patterns

Apply multiple levels of filtering for complex queries:
Mapping
{
  "specific": "$.categories[?(@.name===Electronics)].products[?(@.price>100 && @.inStock===true)]"
}
Explanation
// Step 1: Filter categories by name
$.categories[?(@.name===Electronics)]

// Step 2: From those categories, get products
.products

// Step 3: Filter products by price and stock
[?(@.price>100 && @.inStock===true)]
Chain filters to progressively narrow results.

Dynamic Property Access

Access properties using computed or variable names:
Mapping
{
  "dynamicValue": "$.config[$.trigger.setting_name]"
}
Example Data
{
  "trigger": {"setting_name": "theme"},
  "config": {
    "theme": "dark",
    "language": "en",
    "timezone": "UTC"
  }
}
Result
{
  "dynamicValue": "dark"
}
Access properties dynamically based on trigger data.
Dynamic property access is powerful but can make debugging harder. Always validate that the property names exist and document the expected structure.

Recursive Operations

Work with deeply nested or recursive structures:
Mapping
{
  "allEmails": "$..email",
  "allPrices": "$..price",
  "allIds": "$..id"
}
Use recursive descent (..) to find all occurrences at any nesting level.
When to use recursive descent:
  • Data structure varies or is deeply nested
  • You need all occurrences regardless of location
  • Schema is flexible or unknown
When to avoid:
  • You know the exact path (use direct path for better performance)
  • Working with very large datasets (can be slow)
  • You need only one specific occurrence

Performance Optimization

Optimize your variable mappings for better performance:
Better Performance
{
  // ✅ Specific path - fast
  "email": "$.fetch_user.profile.contact.email"
}
Slower Performance
{
  // ⚠️ Recursive - searches entire structure
  "email": "$..email"
}
Use specific paths when you know the structure.

Performance Best Practices

// ✅ Fast
"$.user.profile.email"

// ⚠️ Slower
"$..email"
// ✅ Filter then extract
"$.items[?(@.active===true)].name"

// ❌ Don't wildcard then filter
"$.items[*].name[?(@.active===true)]"  // Won't work as intended
// ✅ Specific
"$.categories[0].products"

// ⚠️ May return more than needed
"$.categories[*].products"
Store expensive query results in intermediate nodes rather than repeating the same complex expression multiple times.
Performance characteristics change with data size. Test your mappings with realistic data volumes.

Error Handling Strategies

Build robust mappings that handle missing or invalid data gracefully:
{
  "safeEmail": "$.user.profile.contact.email|",
  "safeName": "$.user.firstName| |$.user.lastName|",
  "safeString": "$.items[*].name|"
}
What trailing pipe does:Missing property returns "" (empty string). Array returns comma-separated string "item1,item2". Object returns "[object Object]".This prevents properties from being removed when data is missing, but does NOT provide custom defaults.
Key Points: Pipe operator (|) concatenates strings, does NOT provide defaults. Use trailing pipe $.path| only to prevent property removal when data is missing. For real default values, use flow logic and conditional routing. Use filter existence checks ?(@.property) to safely access nested data.
Common error scenarios to handle: Missing nested properties - Use filters with existence checks ?(@.nested && @.nested.prop) Empty arrays - Check .length before accessing indexes Null values - Filter with ?(@.value!==null) to exclude nulls Type mismatches - Use type selectors ?(@string()) to filter by type Out-of-bounds array access - Check array length, use negative indexes for last items

Complex String Building

Advanced patterns for building dynamic strings:
Mapping
{
  "emailBody": "Dear |$.customer.firstName| |$.customer.lastName|,

Thank you for your order #|$.order.id|!

Order Summary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Items:     |$.order.items.length|
Subtotal:  $|$.order.subtotal|
Tax:       $|$.order.tax|
Shipping:  $|$.order.shipping|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total:     $|$.order.total|

Status: |$.order.status|
Tracking: |$.shipping.tracking|

Questions? Contact us at |$.company.support_email|

Best regards,
|$.company.name| Team"
}

Mapping Strategies for Large Datasets

Handle large arrays and datasets efficiently:
Node 1: Get Slice
{
  "page": "$.api_response.items[$.trigger.offset:$.trigger.offset+$.trigger.limit]"
}
Node 2: Process Page
{
  "processedItems": "$.get_page.page[*].processed_field"
}
Process data in chunks.

Testing and Debugging

Strategies for testing and debugging complex mappings:
Step 1: Test Basic Path
{
  "test": "$.node.data"
}
Step 2: Add Filter
{
  "test": "$.node.data[?(@.active===true)]"
}
Step 3: Add Property Access
{
  "test": "$.node.data[?(@.active===true)].email"
}
Step 4: Add String Building
{
  "test": "Emails: |$.node.data[?(@.active===true)].email|"
}
Build complexity gradually.

Debugging Checklist

1

Verify Data Structure

Use Flow Debugger to inspect actual data from previous nodes
2

Test Path Components

Break complex paths into pieces and test each part
3

Check Filter Logic

Verify filter conditions return expected results
4

Validate String Concatenation

Test pipe operator segments individually
5

Handle Edge Cases

Test with missing data, empty arrays, null values
6

Monitor Performance

Check execution time for complex expressions

Advanced Use Cases

Node 1: Extract
{
  "raw": "$.api_response.data"
}
Node 2: Filter
{
  "filtered": "$.extract.raw[?(@.status===active && @.verified===true)]"
}
Node 3: Transform
{
  "transformed": "$.filter.filtered[*].{id: id, name: name, email: email}"
}
Node 4: Aggregate
{
  "summary": {
    "items": "$.transform.transformed",
    "count": "$.transform.transformed.length",
    "emails": "$.transform.transformed[*].email"
  }
}
Multi-stage transformation with intermediate nodes.

Best Practices Summary

Performance

  • Use specific paths over recursive
  • Filter early in expressions
  • Cache expensive queries
  • Test with realistic data sizes

Reliability

  • Use trailing pipes for optional data
  • Check existence in filters
  • Handle empty arrays
  • Validate assumptions

Maintainability

  • Use descriptive node IDs
  • Document complex mappings
  • Break into steps
  • Test incrementally

Debugging

  • Test pieces individually
  • Use debug output fields
  • Verify data structures
  • Monitor performance

What’s Next?

Questions? Check the Reference or visit our Help Center.