n8n $json Expressions Cheat Sheet (With Examples)
n8n expressions are small pieces of JavaScript inside double curly braces, and nearly all of them start with $json — the JSON of the item a node is working on. This cheat sheet collects the expressions you reach for most, each run against the same sample item so you can see exactly what comes back, plus the errors that trip people up and how to fix them.
Expressions need double braces and Expression mode
An n8n field only evaluates code when it is in Expression mode and the code sits inside double curly braces. {{ $json.searchterm }} returns the value of the searchterm field, while { $json.searchterm } with single braces is plain text and goes out exactly as typed. Text and expressions can share one field, so Order {{ $json.order_id }} is {{ $json.status }} becomes Order 1042 is paid.
$json is the JSON of the current item — shorthand for $input.item.json. Field names are case-sensitive: $json.searchTerm and $json.searchterm are different fields, and the wrong one quietly comes back empty.
The sample item used below
Every example in this cheat sheet runs against this item, so you can check each result yourself:
{
"order_id": 1042,
"status": "paid",
"first name": "Ada",
"customer": { "name": "Ada Lovelace", "email": "ada@example.com" },
"items": [
{ "sku": "KB-01", "qty": 2, "price": 49 },
{ "sku": "MS-07", "qty": 1, "price": 25 }
],
"folder": "exports/2026/october",
"created_at": "2026-10-29T09:30:00Z"
}Read fields, nested objects, and keys with spaces
Dots walk into nested objects, and brackets handle keys that dots can't. Long paths are easier to copy than to type: paste the item into the JSON tree viewer, hover a value, copy its path — such as $.customer.email — and swap the $ for $json inside the braces.
{{ $json.order_id }}returns1042.{{ $json.customer.email }}returnsada@example.com.{{ $json['first name'] }}returnsAda— use brackets for keys with spaces, dashes, or dots.{{ $json.shipping?.city ?? 'none' }}returnsnoneinstead of failing when a parent object is missing.{{ $ifEmpty($json.status, 'unknown') }}returnspaid, orunknownwhen the field is empty — n8n's built-in fallback.
Work with arrays
Arrays use zero-based indexes and the usual JavaScript methods, plus n8n helpers such as .last() and .sum():
{{ $json.items[0].sku }}returnsKB-01, and{{ $json.items.last().sku }}returnsMS-07.{{ $json.items.length }}returns2.{{ $json.items.map(i => i.sku).join(', ') }}returnsKB-01, MS-07.{{ $json.items.map(i => i.qty * i.price).sum() }}returns123.{{ $json.items.filter(i => i.qty > 1).length }}returns1.- To treat each element as its own item in later nodes, use the Split Out node rather than an expression.
Get data from other nodes
$json only sees the current node's input. To read an earlier node, call it by name with $('Node name') — the name must match exactly, spaces and capitals included:
{{ $('Webhook').item.json.body.email }}— the item that produced the current one.{{ $('Fetch orders').first().json.order_id }}— always the first item, whichever one is being processed.{{ $('Fetch orders').all().length }}— how many items that node returned.{{ $('Fetch orders').all().map(i => i.json.order_id).join(', ') }}— one field from every item.{{ $input.first().json.status }}— the first item coming into the current node.- If
.itemfails because n8n can't tell which item matches — common after a Code node that builds new items — use.first(),.last(), or.all()instead. Older workflows write$node['Name'].json;$('Name')is the current form.
Webhook data lives under body, query, and headers
A Webhook node outputs the whole request, so the field you want is one level down. Forgetting body is the most common reason a webhook expression comes back empty:
{{ $json.body.email }}— a field from a JSON or form body.{{ $json.query.page }}— thepagevalue from a URL ending in?page=2.{{ $json.params.id }}— a path parameter, for a webhook path likeorder/:id.{{ $json.headers['user-agent'] }}— header names arrive in lowercase and contain dashes, so use brackets.
Replace text and use regex
Expressions are JavaScript, so string methods behave as they do in code. One catch: .replace() with a plain string changes only the first match — use a regular expression with the g flag, or .replaceAll(), to change every one.
{{ $json.status.toUpperCase() }}returnsPAID, and{{ $json.status.toTitleCase() }}returnsPaid.{{ $json.items[0].sku.replace(/-\d+$/, '') }}returnsKB— a regex that strips a trailing number.{{ $json.folder.replace(/\//g, '\\') }}returnsexports\2026\october. Inside a regex the slash is written\/, and a single backslash in a string is written'\\'.{{ $json.folder.replaceAll('/', '-') }}returnsexports-2026-october.{{ $json.customer.email.split('@')[1] }}returnsexample.com.
Dates with $now, $today, and Luxon
$now and $today are Luxon DateTime objects in the workflow's time zone, and an ISO date string becomes one with .toDateTime():
{{ $now.toFormat('yyyy-MM-dd') }}— today's date, such as2026-10-29.{{ $today.minus(1, 'days').toFormat('yyyy-MM-dd') }}— yesterday.{{ $json.created_at.toDateTime().toFormat('d LLL yyyy') }}returns29 Oct 2026.{{ $now.plus(7, 'days').toISO() }}— a full timestamp one week from now.
Pass JSON on as JSON
When a field needs the JSON itself — an HTTP Request body, a Slack message, a database column — turn the object into a string first, and turn stringified JSON back into an object before reading from it:
{{ $json.customer.toJsonString() }}returns{"name":"Ada Lovelace","email":"ada@example.com"}, and{{ JSON.stringify($json.customer) }}does the same.{{ $json.payload.parseJson().id }}returns7whenpayloadis a string such as{\"id\": 7}.- Got a field escaped like that and want to read it first? The JSON stringify tool unescapes it back into readable JSON.
If/else and multi-line logic
A ternary or n8n's $if() covers a quick either/or. For anything with several steps, wrap plain JavaScript in an immediately invoked function, the pattern the n8n docs recommend:
{{ (() => {
const total = $json.items.reduce((t, i) => t + i.qty * i.price, 0);
return total > 100 ? 'free shipping' : 'standard';
})() }}{{ $json.items.length > 1 ? 'multi' : 'single' }}returnsmulti.{{ $if($json.status === 'paid', 'ship', 'hold') }}returnsship.- The function above returns
free shipping, because the order totals 123. - Past a few lines, move the logic into an Edit Fields (Set) node or a Code node. In a Code node set to Run Once for All Items there is no single current item, so loop over
$input.all()and readitem.json. - Reshaping a whole object into a new structure? The JSON Mapper builds the mapping visually and generates JavaScript you can adapt for a Code node.
Expression errors and how to fix them
Most broken expressions fail in one of a few ways:
- “Can't get data for expression” — the expression reads a node that has not run yet. Run the workflow up to that node, or check
$('Node name').isExecutedfirst. - “Referenced node is unexecuted” — the named node is not on the branch that ran. Rewire the workflow so it executes first, or read the value from a node that did run.
- “Invalid syntax” — usually a stray character, such as the trailing dot in
{{ $('If').item.json. }}. - An empty result with no error — the path is wrong. Check spelling and capitals, whether webhook data sits under
body, and whether the value is inside an array that needs an index. {{ $env.MY_VAR }}is refused — since n8n 2.0,N8N_BLOCK_ENV_ACCESS_IN_NODEdefaults totrue, so a self-hosted instance blocks environment variables in nodes unless you change that setting.
Run n8n on your own server
Every expression here works the same on n8n Cloud and on a self-hosted instance. If you would rather keep your workflows, and the data they touch, on a machine you control, the self-hosting section of our deploy guide walks through running n8n in Docker on a VPS, with a persistent volume so nothing is lost on restart.
Try it yourself
Turn JSON into an interactive, searchable tree you can expand and collapse.