Expressions: Pull Data Between Nodes
In the last lesson you saw values like ={{ $json.full_name }} quietly doing the real work. Those are expressions, and they are how data moves from one node to the next. If items are the nouns of n8n, expressions are the verbs. Once you can read and write expressions, you can connect any two nodes together, no matter what fields they carry.
What You'll Learn
- What an n8n expression is and the exact
{{ }}syntax - How
$jsonrefers to fields on the current item - How to reference fields from earlier nodes, not just the previous one
- How to combine fields and apply small transformations inside an expression
- Hands-on practice transforming sample item data in the editor below
The syntax
Anywhere a node field accepts a value, you can switch it from a fixed value to an expression. An expression is wrapped in double curly braces:
{{ $json.fieldName }}
Inside the braces you write a small piece of logic, almost always plain JavaScript. The most important variable available to you is $json, which is the json object of the current item flowing into the node. So if the current item is:
{ "json": { "firstName": "Ada", "lastName": "Lovelace", "signups": 3 } }
then these expressions produce:
| Expression | Result |
|---|---|
{{ $json.firstName }} | Ada |
{{ $json.lastName }} | Lovelace |
{{ $json.signups }} | 3 |
{{ $json.signups * 10 }} | 30 |
{{ $json.firstName + " " + $json.lastName }} | Ada Lovelace |
That is the whole core idea: $json.something reaches into the current item and pulls out a field, and you can do normal JavaScript math and string work right there in the braces.
A small but important detail you saw in the starter workflow: inside the workflow JSON, an expression value starts with a leading =, like "value": "={{ $json.firstName }}". The = is how the saved file marks a field as "this is an expression." In the editor UI you simply toggle a field to expression mode and type the {{ ... }} part; the = is handled for you. When you read raw workflow JSON in this course, just remember = means "expression follows."
Referencing other nodes
$json always means the current item from the node directly before. But often you need a value from a node further back, for example an email address captured by the trigger, used many steps later. For that, n8n lets you reference a node by name:
{{ $('When clicking Test').item.json.email }}
Read that as: "from the node named When clicking Test, take the current item's json, and read its email field." This is extremely common in real workflows, because data you need at the end was often captured at the very start. You do not have to drag fields through every node in between; you just reach back to the node that has them.
So you have two everyday tools:
{{ $json.x }}for "the fieldxon the item right here," and{{ $('Node Name').item.json.x }}for "the fieldxfrom a specific earlier node."
Practice: think in expressions
Expressions are just JavaScript operating on a json object, so you can practice the exact logic right here. The editor below defines a sample item the way n8n would, then evaluates expressions against it. Run it, read the output, then edit the expressions at the bottom and run again.
Try the challenge before reading on. A short name is $json.firstName.charAt(0) + ". " + $json.lastName, and the pro check is $json.plan === "pro". In a real workflow you would paste the part inside console.log(...) into a node field wrapped in {{ }}, and n8n would compute it for every item automatically.
Where you actually type expressions
Expressions are not a special node. They live inside the fields of ordinary nodes:
- In a Set node, the value of a field can be an expression:
={{ $json.signups * 10 }}. - In an HTTP Request node, the URL can be an expression:
=https://api.example.com/users/{{ $json.userId }}. - In a Gmail node, the subject can be an expression:
=New signup from {{ $json.firstName }}.
This is what makes a static workflow dynamic. The same workflow handles Ada, Alan, and Grace differently because each field is computed from the current item rather than hard-coded.
A worked example
Suppose a trigger gives you items shaped like { "json": { "name": "Grace Hopper", "amount": 49 } } and you want to send a thank-you with the amount formatted nicely. In a Set node you would create:
| Field name | Value (expression) |
|---|---|
| greeting | =Thank you, {{ $json.name }}! |
| paid | ={{ "$" + $json.amount }} |
| tier | ={{ $json.amount >= 50 ? "premium" : "standard" }} |
Run that against Grace's item and you get greeting: "Thank you, Grace Hopper!", paid: "$49", and tier: "standard". Change the amount to 50 and tier flips to premium, because the expression decided it, item by item.
Key Takeaways
- An expression is logic wrapped in
{{ }}that computes a field's value from data. $json.fieldNamereads a field from the current item; you can do normal JavaScript math and string operations inside the braces.- Reference earlier nodes with
{{ $('Node Name').item.json.fieldName }}to reach data captured upstream. - In saved workflow JSON, a leading
=marks a value as an expression; the editor adds it for you. - Expressions live inside ordinary node fields (URLs, subjects, values), which is what turns one static workflow into a dynamic one that handles every item correctly.

