Docs / Strand / nodes/transform
Transform Node
The Transform node maps and transforms event data by creating new payloads from existing data.
Each mapping value is an expression evaluated against the incoming payload.
Overview
Transform nodes take input events and create new output events with transformed data structures.
- Reshape data structures
- Combine data from multiple sources
- Calculate derived values
- Normalize data formats
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
mapping |
object | Yes | Object mapping new field names to expressions |
Mapping Format
The mapping field is a JSON object where:
- Keys = New field names in the output
- Values = Jinja2 expressions that generate the values
{
"new_field": "{{ expression }}",
"another_field": "{{ another_expression }}"
}
The output payload contains only the keys you map; every field of the input payload that you don't reference is dropped. (Event meta is carried through unchanged.) To keep the original payload alongside your new fields, map it explicitly:
{
"new_field": "{{ payload.something }}",
"_original": "{{ payload | tojson }}"
}
Basic Examples
Simple Field Mapping
Input:
{
"first_name": "John",
"last_name": "Doe"
}
Mapping:
{
"full_name": "{{ payload.first_name }} {{ payload.last_name }}",
"email": "{{ payload.email }}"
}
Output:
{
"full_name": "John Doe",
"email": "[email protected]"
}
Using Direct Connection
When directly connected to the previous node, use payload:
Mapping:
{
"user_id": "{{ payload.id }}",
"email": "{{ payload.email }}",
"name": "{{ payload.first_name }} {{ payload.last_name }}"
}
Using Non-Direct Steps
When accessing data from a non-directly connected node:
Mapping:
{
"user_id": "{{ steps.user_lookup.output_payload.id }}",
"email": "{{ steps.user_lookup.output_payload.email }}",
"name": "{{ steps.user_lookup.output_payload.first_name }} {{ steps.user_lookup.output_payload.last_name }}"
}
Calculations
Mapping:
{
"total": "{{ payload.price * payload.quantity }}",
"tax": "{{ payload.price * payload.quantity * 0.1 }}",
"grand_total": "{{ payload.price * payload.quantity * 1.1 }}"
}
Conditional Values
Mapping:
{
"status": "{{ 'active' if payload.enabled == true else 'inactive' }}",
"priority": "{{ 'high' if payload.amount > 1000 else 'normal' }}"
}
Advanced Examples
Combining Multiple Sources
Mapping:
{
"user": {
"id": "{{ steps.user_lookup.output_payload.id }}",
"email": "{{ steps.user_lookup.output_payload.email }}"
},
"preferences": {{ steps.preferences_lookup.output_payload | tojson }},
"metadata": {
"source": "{{ initial.meta.trigger_source }}",
"workflow": "{{ initial.meta.workflow_name }}"
}
}
Array Transformations
Mapping:
{
"item_count": "{{ payload.items | length }}",
"total_value": "{{ payload.items | sum(attribute='price') }}",
"item_ids": "{{ payload.items | map(attribute='id') | list }}"
}
JSONPath Integration
Mapping:
{
"active_user_emails": "{{ payload | jsonpath('$.users[?(@.active == true)].email') }}",
"user_count": "{{ payload | jsonpath('$.users.`len`') }}"
}
Best Practices
- Use descriptive field names
- Keep mappings simple and readable
- Test with sample data
- Use
defaultfilter for missing data:
{
"email": "{{ payload.email | default('[email protected]') }}"
}
- Document complex transformations with comments in expressions
Related
- Passing Data Between Steps - Accessing previous step outputs
- Jinja2 Syntax - Expression syntax
- JSONPath - Complex data queries
Tendrl