Working with Data: Expressions, JSON, and Transformations
Most workflow bugs in n8n are “data shape” bugs: a field isn’t where you think it is, a value is a string instead of a number, or you’re handling an array like a single object. Earlier you learned why items and normalization checkpoints matter; here you’ll learn the hands-on tools to read, reference, and reshape data reliably.
1) Reading n8n data: JSON structure you actually work with
In almost every node, the meaningful payload lives under an item’s json.
Key patterns you’ll see:
Flat objects
- Example fields:
id,
email,
status
Nested objects
- Example:
customer.email,
customer.address.city
Arrays (lists)
- Example:
lineItems[0].sku,
tags (array of strings)
A practical way to “locate” data:
When something fails, inspect the node’s input/output data in the execution view and confirm:
Does the field exist?
Is it under the path you expect?
Is it a single object or an array?2) Expressions: referencing data dynamically
Expressions are how you pull values from runtime data into node fields (URLs, message bodies, conditions, filenames, etc.).
The most common expression references
Current item: node["Node Name"].json
- Use when you must reference a specific node, not just “previous”.
- Especially helpful when you have branches/merges.
Metadata / time: utilities like node["..."] stays understandable.3) Transformations: shaping data without chaos
Transformations are where you standardize fields, rename keys, compute derived values, and restructure arrays/objects.
The “keep it explicit” rule
A good transformation step:
Produces a known schema (fixed field names).
Removes or isolates raw payload noise.
Makes downstream nodes simpler.A simple pattern is to preserve the original payload and add your normalized fields:
Common transformation operations (and when to use them)
Rename fields
- When upstream uses inconsistent keys (
mail,
email_address,
email).
Pick/omit fields
- When downstream only needs 5 fields, don’t pass 50.
Compute derived fields
- Examples:
fullName,
isPaid,
summaryText,
priority.
Flatten nested data
- Turn
customer.email into
customerEmail for easier mapping.
Reshape arrays
- Examples:
1. Convert an array of line items into a printable string.
2. Extract the first matching element.
3. Turn a list into one aggregated record (when you need a single summary item).
Where transformations “live” in n8n
You can transform data in multiple places:
Dedicated transform nodes (recommended for maintainability)
- Clear, visible “data contract” step.
Inline expressions inside action nodes
- Fine for simple mappings, risky for complex logic.
If you notice repeated expressions across multiple nodes, consolidate them into one transformation step so you have one place to fix.
4) Practical pitfalls (and how to avoid them)
Overwriting fields unintentionally
- If you reuse names like
id or
status, you may break downstream logic. Prefer names that encode meaning (
paymentStatus,
orderStatus).
Assuming one item when you have many
- Many nodes run “per item”. If you need one combined message, aggregate before sending.
Loose comparisons
-
"10" vs
10 can change behavior. Normalize types early.
Branch + merge confusion
- After merges, “previous node” references can be misleading. Use
json is safer than referencing
json safer: inside a loop-like flow where each item is processed independently; you want the value for “this item” regardless of how the graph branches.
-
$node["Some Node"].json safer: after merges/branches, or when multiple upstream paths exist and you must guarantee you’re reading from a specific node’s output.
If it’s just one field (name with fallback) used once, an inline expression is fine. If the fallback name is used in multiple places (Slack + email + ticket), create a dedicated transformation step that sets a stable field like displayName so all actions reuse it.Example set:
1. Pick only needed fields (omit noisy/unused keys).
2. Flatten nested values (e.g.,
customer.contact.email →
customerEmail).
3. Normalize types (numbers/dates/booleans) and compute derived flags (e.g.,
isPaid).
Two techniques:
1. Optional/defensive access for nested fields (don’t assume
customer or
address exists).
2. Default values (e.g., fallback to an empty string or “Unknown city”) after attempting to read the nested field.
</details>