Understand how data flows between n8n nodes. Master expressions, the Code node and filtering — these are the foundations of every effective workflow. This guide is written for UK users building realistic automations over WooCommerce orders, HMRC-compliant invoices, CRM sync and reporting pipelines.
How data flows between nodes • Using n8n expressions to transform data • The Set and Code nodes • Filtering and branching with IF and Switch • UK-specific data transformations • Common data patterns • FAQ
To work effectively with n8n, you need to understand one fundamental concept: every node processes an array of items. Each item is a JSON object with a json field (the data) and an optional binary field (files).
When a webhook receives an order, that’s one item. When Google Sheets returns 50 rows, that’s 50 items — each with its own data. When you connect two nodes, n8n passes all items from the first node’s output to the second node’s input.
A processing node by default operates on each item individually — if you have 50 items and send an email for each, the Email node runs 50 times. This is one-to-one processing, and it’s why automation scales naturally: whether 1 order or 1,000, the workflow looks the same.
Tip: in each node’s Output panel you see tabs numbered by item (Item 1, Item 2...) with the JSON structure of each. Click the eye icon on a node after execution — you see exactly what went in and what came out. It’s the most important tool for debugging workflows and figuring out where to grab data for the next step.
Data from one node is available in the next through the $json variable (current item) or by referencing a specific node by name: $('Node Name').item.json. Nested fields use dot notation: $json.customer.address.city. Array fields use indices: $json.products[0].name. This notation works in expressions across all n8n nodes.
In every text field of an n8n node you’ll see a small icon with ${}. Click it — the field switches to expression mode and opens an editor with preview. You type the expression on the left; you see the result for the current data item on the right. This lets you build expressions iteratively, seeing live whether they return the right values.
{{ $json.fieldName }} — access a field from the current item{{ $json.user.email }} — nested field{{ $json.items[0].price }} — array element{{ $('Webhook').item.json.orderId }} — data from a named nodeIn the expression editor’s left panel, you also get a list of available variables — click one to insert it into your expression.
In double curly braces, you have full JavaScript. Examples:
{{ $json.firstName + ' ' + $json.lastName }}{{ Math.round($json.price * 1.20) }} (price including UK VAT at 20%){{ new Date($json.createdAt).toLocaleDateString('en-GB') }} (produces DD/MM/YYYY){{ parseInt($json.quantity) * parseFloat($json.unitPrice) }}{{ $json.email.toLowerCase() }}, {{ $json.name.trim() }}n8n provides several helpers:
$now — current date/time as a Luxon object. {{ $now.toISO() }}, {{ $now.minus({days: 7}).toISO() }} (a week ago)$today — today at midnight UK time$workflow — metadata about the workflow itself (id, name)$execution — metadata about this execution$vars — reference global variables set in Settings › Variables — ideal for API tokens and configuration reused across workflows. {{ $vars.API_KEY }} injects the variable without exposing it in the workflow code itself.Your fundamental tool for manipulating data without writing code. Lets you create new fields, overwrite existing ones, and remove unnecessary ones.
Add a Set node, click Add Field, give the new field a name and a value (static or expression). Tick Keep Only Set Fields if you want to clear all other fields and keep only the ones you’re setting — useful for simplifying data before sending to an external API.
Example: after receiving an order via webhook, you want to prepare data for emailing. Set creates the fields:
customerName: {{ $json.billing.first_name + ' ' + $json.billing.last_name }}orderTotal: £{{ ($json.total).toFixed(2) }}orderDate: {{ new Date($json.date_created).toLocaleDateString('en-GB') }}The next Email node can use these fields directly, without knowing the original WooCommerce order structure.
Full JavaScript (or Python on recent n8n versions). Use it when you need logic impossible to express in the Set node: loops over arrays, aggregation (summing order values), creating new items from existing ones, calls to n8n helper functions.
Data access:
const items = $input.all() returns the array of all items$input.first().json returns the data of the first itemAlways return an array: return [{json: {total: 1234, count: 10}}].
Example Code node: calculate total revenue from a list of orders.
const sum = $input.all().reduce((acc, item) =>
acc + parseFloat(item.json.total), 0);
const count = $input.all().length;
return [{
json: {
totalRevenue: sum.toFixed(2),
orderCount: count,
averageOrderValue: (sum / count).toFixed(2)
}
}];Output: one item with the total revenue, order count and average order value — ready to format into a Slack daily-report message.
Conditional branching — routes items to one of two paths (True or False) depending on whether a condition is met. Add IF after a node supplying data, configure the condition (value1 operator value2), and connect the True and False outputs to the appropriate next nodes.
Example uses:
You can combine multiple conditions with AND or OR — all must match (AND) or just one (OR).
Multi-way branching — the equivalent of switch/case from programming. Configure one field to check and a list of possible values. Each value creates a separate output.
Example: an order status field might have values “new” (output 0: send confirmation), “paid” (output 1: prepare for dispatch), “shipped” (output 2: send tracking number), “cancelled” (output 3: refund). You can also add a Fallback output for unknown values.
Common UK business data transformations you’ll use in realistic workflows:
{{ new Date($json.date).toLocaleDateString('en-GB') }} for DD/MM/YYYY. Never use US format (MM/DD/YYYY) for UK customers.{{ '£' + ($json.amount).toFixed(2) }}. For Intl.NumberFormat: {{ new Intl.NumberFormat('en-GB', {style:'currency', currency:'GBP'}).format($json.amount) }}{{ ($json.netPrice * 1.20).toFixed(2) }} for net-to-gross. {{ ($json.grossPrice / 1.20).toFixed(2) }} for gross-to-net.{{ /^[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}$/i.test($json.postcode) }} returns true for valid UK postcodes.{{ $json.phone.replace(/^0/, '+44').replace(/\s/g, '') }} converts 07700900123 to +447700900123.{{ $now.setZone('Europe/London').toISO() }} for UK local time.{{ /^GB\d{9}$/.test($json.vatNumber) }} (basic check; use a proper VIES lookup for full validation).n8n on UK infrastructure
Build automations that handle UK GDPR properly — self-hosted n8n on SmartXHosting keeps your data on UK servers. Pre-configured, SSL-protected, UK support team.
See n8n hosting plansQ: Why does my workflow only process one item when I expected many?
A: You’re probably in “Run Once” test mode or a node is set to execute once. Check the node’s Execute setting; use a test that produces the full dataset.
Q: Can I use the Python version of Code node?
A: Recent n8n versions support both JavaScript and Python (with Pyodide). JavaScript is better supported and faster for most cases; Python makes sense when you have existing Python code or libraries to use.
Q: How do I handle sensitive data like API keys?
A: Use Settings › Variables for shared secrets, or n8n Credentials for service-specific secrets (Gmail, Slack OAuth tokens). Never hardcode API keys in expressions or workflow JSON.
Q: My expression works in preview but not at runtime.
A: The preview shows the current cached data from the last execution. At runtime, data might be different (different input item, different trigger). Enable “Always Output Data” on upstream nodes and check executions for the real runtime data.
Q: How do I format currency for UK customers?
A: {{ new Intl.NumberFormat('en-GB', {style:'currency', currency:'GBP'}).format($json.amount) }} produces £42.00. Or simpler: {{ '£' + ($json.amount).toFixed(2) }}.
Q: Can I test an expression before saving?
A: Yes — the expression editor has a live preview on the right. Adjust the expression until the preview shows what you want.
Q: When should I use Set vs Code?
A: Set is enough for 80% of transformations: creating fields, simple formulas, formatting. Use Code when you need loops, aggregation, conditional item creation, or external libraries.
Q: How do I handle errors in expressions?
A: Use the optional chaining operator ?. to guard against missing fields: {{ $json.customer?.email ?? '[email protected]' }} returns the fallback if customer or email is missing. Configure node-level error handling on the Settings tab.