Data Mapping
Data mapping defines how information transforms as it flows between nodes. Master this to build powerful automations.
How Data Mapping Works
When you create an edge between nodes, you configure how source data maps to the target:
┌──────────────┐ ┌──────────────┐
│ Node A │ Edge │ Node B │
│ │ Mapping ──────▶ │ │
│ Output: { │ Transform: │ Input: { │
│ user: { │ userId: $.user.id│ userId: │
│ id: 123 │ name: $.user.name│ name: │
│ name:"" │ │ } │
│ } │ │ │
│ } │ │ │
└──────────────┘ └──────────────┘Basic Mapping
Pass-Through
Send all data unchanged:
data: "{{$}}"The $ represents the entire source data.
Select Fields
Pick specific fields:
# Source: { user: { id: 123, name: "Alice", email: "a@b.com" } }
userId: "{{$.user.id}}"
userName: "{{$.user.name}}"
# Result: { userId: 123, userName: "Alice" }Rename Fields
Map to different names:
# Source: { firstName: "Alice", lastName: "Smith" }
givenName: "{{$.firstName}}"
familyName: "{{$.lastName}}"
# Result: { givenName: "Alice", familyName: "Smith" }Expression Syntax
Path Access
# Object property
value: "{{$.propertyName}}"
# Nested property
value: "{{$.parent.child.grandchild}}"
# Array element
value: "{{$.items[0]}}"
# Last array element
value: "{{$.items[-1]}}"String Interpolation
Embed values in strings:
message: "Hello, {{$.userName}}!"
url: "https://api.example.com/users/{{$.userId}}"Pure Expressions
Return typed values (not strings):
# Returns number, not string
count: {{$.items.length}}
# Returns boolean
enabled: {{$.status == "active"}}
# Returns object
user: {{$.data.user}}Note: Without quotes, the value type is preserved. With quotes, it becomes a string.
Type Handling
Type Preservation
| Expression | Result Type |
|---|---|
{{$.count}} | number |
"{{$.count}}" | string |
{{$.active}} | boolean |
"{{$.active}}" | string |
{{$.user}} | object |
Type Conversion
Convert between types:
# Number to string
countStr: "{{string($.count)}}"
# String to number (if valid)
amount: {{int($.priceStr)}}
# Boolean check
hasItems: {{$.items.length > 0}}Transformations
String Operations
# Uppercase
upper: "{{upper($.name)}}"
# Lowercase
lower: "{{lower($.name)}}"
# Concatenation
full: "{{$.first}} {{$.last}}"
# Template
greeting: "Dear {{$.title}} {{$.lastName}},"Numeric Operations
# Math
total: {{$.price * $.quantity}}
discount: {{$.total * 0.1}}
final: {{$.total - $.discount}}
# Rounding
rounded: {{round($.value)}}Date Operations
# Current time
timestamp: "{{now()}}"
# Format date
formatted: "{{RFC3339($.createdAt)}}"Array Operations
# Array length
count: {{$.items.length}}
# First element
first: {{$.items[0]}}
# Filter (conceptual)
active: {{filter($.users, "status == 'active'")}}Object Mapping
Restructure Objects
# Source:
# {
# data: {
# user: { id: 1, name: "Alice" },
# meta: { timestamp: "..." }
# }
# }
# Target mapping:
user:
id: "{{$.data.user.id}}"
name: "{{$.data.user.name}}"
timestamp: "{{$.data.meta.timestamp}}"
# Result:
# {
# user: { id: 1, name: "Alice" },
# timestamp: "..."
# }Nested Objects
response:
success: true
data:
items: {{$.results}}
count: {{$.results.length}}
metadata:
timestamp: "{{now()}}"Spread/Merge
# Include all source fields plus new ones
...data: {{$}}
extraField: "added"Conditional Mapping
Ternary Operator
status: "{{$.active ? 'enabled' : 'disabled'}}"
type: "{{$.amount > 100 ? 'large' : 'small'}}"Default Values
# Use default if null/undefined
name: "{{$.name || 'Unknown'}}"
count: {{$.items.length || 0}}Null Handling
# Check for existence
hasUser: {{$.user != null}}
# Safe navigation (if supported)
email: "{{$.user?.email || 'no-email'}}"Common Patterns
HTTP Request Mapping
# Map incoming HTTP request
method: "{{$.method}}"
path: "{{$.path}}"
headers: {{$.headers}}
body: {{$.body}}
queryParams: {{$.queryParams}}
clientIP: "{{$.realIP}}"HTTP Response Building
# Build response
body: |
{
"success": true,
"data": {{$.result}},
"timestamp": "{{now()}}"
}
contentType: "application/json"
statusCode: 200Event Transformation
# Transform webhook event
eventType: "{{$.type}}"
timestamp: "{{$.created_at}}"
source: "webhook"
data:
id: "{{$.data.id}}"
action: "{{$.action}}"Database Record Mapping
# Map for database insert
record:
id: "{{$.userId}}"
email: "{{$.email}}"
created_at: "{{now()}}"
metadata: {{$.additionalInfo}}Mapping Editor
Visual Mode
The edge properties panel provides visual mapping:
┌─────────────────────────────────────────────────┐
│ Data Mapping │
├─────────────────────────────────────────────────┤
│ Target Schema Source Expression │
│ ┌────────────────┐ ┌────────────────────┐ │
│ │ userId │ ← │ $.user.id │ │
│ └────────────────┘ └────────────────────┘ │
│ ┌────────────────┐ ┌────────────────────┐ │
│ │ email │ ← │ $.user.email │ │
│ └────────────────┘ └────────────────────┘ │
│ ┌────────────────┐ ┌────────────────────┐ │
│ │ timestamp │ ← │ now() │ │
│ └────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────┘Code Mode
Switch to YAML/JSON for complex mappings:
userId: "{{$.user.id}}"
email: "{{$.user.email}}"
profile:
name: "{{$.user.firstName}} {{$.user.lastName}}"
avatar: "{{$.user.avatarUrl || 'default.png'}}"
settings: {{$.user.preferences}}Schema Hints
The editor shows expected schema:
Target expects:
{
userId: number (required)
email: string (required)
profile: {
name: string
avatar: string
}
}Debugging Mappings
Use Debug Node
Add a Debug node to inspect data:
Source ──▶ Debug ──▶ Target
│
└─▶ View actual data in logsCheck Types
Common type issues:
# ❌ Type mismatch - returns string
count: "{{$.items.length}}"
# ✅ Correct - returns number
count: {{$.items.length}}Validate Paths
Test expressions:
- Add Debug node before mapping
- Check actual data structure
- Verify path exists
- Update mapping accordingly
Best Practices
1. Start Simple
Begin with basic mappings, then refine:
# Start with
data: {{$}}
# Then refine to
userId: "{{$.user.id}}"2. Handle Missing Data
Always consider null/undefined:
email: "{{$.email || 'not-provided'}}"
count: {{$.items.length || 0}}3. Document Complex Mappings
Add comments for clarity:
# Extract user for notification service
userId: "{{$.data.user.id}}"
# Format full name from parts
fullName: "{{$.data.user.first}} {{$.data.user.last}}"
# Use current timestamp if not provided
timestamp: "{{$.timestamp || now()}}"4. Test Edge Cases
Verify mappings handle:
- Empty arrays
- Null values
- Missing fields
- Unexpected types
Next Steps
- Expression Syntax - Full expression reference
- Operators and Functions - Available operations
- Expression Examples - Real-world examples