Understanding how data flows through n8n and how to transform it with expressions and code is the key skill that separates basic automations from powerful ones.
Data in n8n flows as an array of items. Each item has two parts:
// A typical item structure
{
json: {
id: 42,
name: "Alice",
email: "alice@example.com",
score: 95
},
binary: {
// optional: file attachments, images, etc.
data: { mimeType: "image/png", data: "base64string..." }
}
}
When an HTTP Request returns an array of 10 users, n8n creates 10 items — one per user. Most nodes process each item individually in a loop.
Expressions use double curly braces {{ }} and give you access to dynamic data. Click the lightning bolt icon next to any field to switch from a static value to an expression.
# Access current item fields
{{ $json.name }}
{{ $json.user.email }}
{{ $json["field-with-dashes"] }}
# Access a specific node's output
{{ $node["HTTP Request"].json.id }}
{{ $('Fetch Users').item.json.email }}
# Access item by position
{{ $('Fetch Users').first().json.name }}
{{ $('Fetch Users').last().json.name }}
# Modern syntax (n8n v1+)
{{ $('NodeName').item.json.field }}
{{ $('NodeName').all()[0].json.field }}
# Legacy syntax (still works)
{{ $node["NodeName"].json.field }}
# Example: get the ID from step 2 while in step 4
{{ $('Fetch Product').item.json.product_id }}
# Access response from HTTP Request node
{{ $('Call API').item.json.data.users[0].name }}
| Variable | Description | Example output |
|---|---|---|
$json | Current item's JSON data | { name: "Alice" } |
$now | Current datetime (Luxon object) | 2024-06-15T09:30:00.000Z |
$today | Today's date at midnight | 2024-06-15T00:00:00.000Z |
$runIndex | Which run of the workflow (0-based) | 0 |
$itemIndex | Index of current item in the batch | 3 |
$workflow.id | Current workflow's ID | "abc123" |
$execution.id | Current execution's ID | "exec_xyz" |
n8n uses Luxon for date handling. $now and $today are Luxon DateTime objects.
# Format dates
{{ $now.toISO() }} # "2024-06-15T09:30:00.000Z"
{{ $now.toFormat('yyyy-MM-dd') }} # "2024-06-15"
{{ $now.toFormat('dd/MM/yyyy HH:mm') }} # "15/06/2024 09:30"
# Date arithmetic
{{ $now.minus({ days: 7 }).toISO() }} # 7 days ago
{{ $now.plus({ hours: 2 }).toISO() }} # 2 hours from now
{{ $now.startOf('month').toISO() }} # First day of month
{{ $now.endOf('week').toISO() }} # End of current week
# Parse an existing date string
{{ DateTime.fromISO($json.created_at).toFormat('MMM d, yyyy') }}
{{ DateTime.fromFormat($json.date, 'dd/MM/yyyy').toISO() }}
# Standard JavaScript string methods
{{ $json.name.toUpperCase() }} # "ALICE"
{{ $json.name.toLowerCase() }} # "alice"
{{ $json.email.split('@')[1] }} # "example.com"
{{ $json.title.includes('urgent') }} # true/false
{{ $json.message.replace('foo','bar') }} # replace text
{{ $json.name.trim() }} # strip whitespace
{{ $json.code.padStart(6,'0') }} # "000042"
# Template literals
{{ `Hello ${$json.name}, score: ${$json.score}` }}
# Conditional (ternary)
{{ $json.score >= 70 ? 'Pass' : 'Fail' }}
When expressions aren't enough, the Code node gives you a full JavaScript environment. Access all input items via $input.all() and return a new array of items.
// Code node — Run Once for All Items mode
const items = $input.all();
const result = items.map(item => {
const data = item.json;
return {
json: {
fullName: `${data.firstName} ${data.lastName}`,
emailDomain: data.email.split('@')[1],
isHighValue: data.orderTotal > 1000,
processedAt: new Date().toISOString()
}
};
});
return result;
json key. Forgetting this is the most common Code node mistake.// Filter: only keep items where score > 70
const items = $input.all();
return items
.filter(item => item.json.score > 70)
.map(item => ({ json: item.json }));
// Group items by category
const grouped = {};
$input.all().forEach(item => {
const cat = item.json.category;
if (!grouped[cat]) grouped[cat] = [];
grouped[cat].push(item.json);
});
return Object.entries(grouped).map(([cat, rows]) => ({
json: { category: cat, count: rows.length, items: rows }
}));
// Flatten a nested array from an API response
const raw = $input.first().json.data.users;
return raw.map(user => ({ json: user }));
| Task | Expression |
|---|---|
| Current timestamp (ISO) | {{ $now.toISO() }} |
| Item field | {{ $json.fieldName }} |
| Nested field | {{ $json.user.address.city }} |
| Array element | {{ $json.tags[0] }} |
| Conditional value | {{ $json.active ? 'Yes' : 'No' }} |
| From another node | {{ $('Step2').item.json.id }} |
| String interpolation | {{ `Order #${$json.id}` }} |
| Math operation | {{ $json.price * $json.qty }} |
| Null default | {{ $json.name ?? 'Unknown' }} |
| 7 days ago | {{ $now.minus({days:7}).toISO() }} |