The Mental Model: How n8n “Thinks” About Data
You are staring at the n8n canvas. You’ve successfully connected a Webhook to a Google Sheet. However, there is a problem.
The date format is wrong. The customer name is lowercase. Your IF node keeps returning false when it should be true.
You try to fix it. You type a code snippet, but n8n returns an error or undefined. Welcome to the Expression Editor.
This is the heartbeat of n8n. While n8n is famous for its visual interface, its true power lies in n8n expressions and variables. Understanding this language is vital. It is essentially JavaScript wrapped in a templating engine.
Mastering this skill is the difference between building a simple toy workflow and architecting a scalable enterprise ecosystem. At Thinkpeak.ai, we build these ecosystems every day.
We rely on advanced expressions to transform static data into dynamic business value. This guide is your manual. We will move beyond the basics and dive deep into the syntax and architecture of variables.
The Mental Model: How n8n “Thinks” About Data
Before writing code, you must understand the invisible structure holding your data. The main reason expressions fail is a misunderstanding of n8n’s data structure.
The “Array of Objects” Rule
Every node in n8n receives data in a specific format: An Array of Objects.
Even if your API returns a simple list, n8n wraps it. Standard JSON looks like a simple object with keys and values. However, n8n places that object inside a json key within an array.
When you write an expression, you are usually trying to access data inside that json key. If you reference the top-level object directly without understanding this wrapper, your workflow will break.
Item Linking (The “Magic” Context)
n8n tries to be helpful. When you reference a variable like $json.email, n8n automatically guesses which “Item” in the list you mean.
If you have 10 incoming items, the node runs 10 times. In Run 1, the variable refers to Item 1. In Run 2, it refers to Item 2.
The trap occurs if you break this link using an Aggregate node. You can no longer use simple shortcodes. You must use specific selectors.
The Dictionary: Essential n8n Variables and Selectors
To control your data, you need to know which selector to use. Here is the hierarchy of essential n8n variables.
1. $json (The Daily Driver)
This is the shorthand for “the data inside the current item.” use this for simple mappings where one input equals one output.
Usage:{{ $json.myField }}
2. $node (The Time Traveler)
This allows you to pull data from any previous node in your workflow. It is not limited to the node immediately connected to the current one.
Usage:{{ $node["Webhook"].json.body.order_id }}
Pro Tip: Always rename your nodes to something descriptive. For example, use “Get_Apollo_Data” instead of “HTTP Request 2”. It makes your expressions readable and debugging easier.
3. $input.all() (The Batch Selector)
This variable accesses all items arriving at the node as a single array. It does not process them one by one.
Usage:{{ $input.all()[0].json.id }}
Use this when you need to calculate a sum or aggregate a list of emails. It is also useful when comparing one item against the whole list.
4. $vars (The Configurator)
These are global variables you set for the execution. Use this for configuration settings that might change, like API keys or Channel IDs.
Usage:{{ $vars.slack_channel_id }}
Mastering the Expression Editor (JavaScript Logic)
The n8n Expression Editor accepts JavaScript logic. This means you aren’t limited to just selecting fields; you can transform them.
String Manipulation
You often need to clean your data before it hits your CRM.
Capitalize First Letter:
{{ $json.name.charAt(0).toUpperCase() + $json.name.slice(1) }}
Split a Full Name:
// First Name
{{ $json.fullName.split(" ")[0] }}
// Last Name
{{ $json.fullName.split(" ").slice(1).join(" ") }}
Regex Extraction:
{{ $json.subject.match(/Order #(d+)/)[1] }}
Complex string manipulation is often required for scraping and enrichment. Our Cold Outreach tools use advanced regex patterns to scrape prospect data. Instead of building these from scratch, you can deploy our pre-architected systems at Thinkpeak.ai.
Math and Numbers
Rounding Currency:
{{ Math.round($json.price * 100) / 100 }}
Calculating Margins:
{{ ($json.revenue - $json.cost) / $json.revenue * 100 }}%
Logic (Ternary Operators)
You can replace entire IF nodes with a single line of code. This cleans up your canvas significantly.
Syntax: condition ? value_if_true : value_if_false
Example:
{{ $json.deal_size > 1000 ? "High Priority" : "Standard" }}
Date & Time Manipulation with Luxon
Dates are the nemesis of automation. Different APIs use different formats. n8n uses a library called Luxon to handle this.
The $now and $today Objects
$now: Returns the current date and time in ISO format.$today: Returns the current date with the time set to 00:00:00.
Common Luxon Formulas
Format a Date (DD/MM/YYYY):
{{ $now.toFormat('dd/MM/yyyy') }}
Calculate a Deadline (Today + 7 Days):
{{ $now.plus({ days: 7 }).toISO() }}
Parse a Weird Format:
If an API sends “12-25-2025”, n8n might treat it as a string. You must parse it to do math on it.
{{ DateTime.fromFormat($json.dateString, 'MM-dd-yyyy').toFormat('yyyy-MM-dd') }}
Timezone errors can kill a marketing campaign. We strictly use ISO 8601 formats internally at Thinkpeak.ai. This ensures our AI agents never miss a beat across different time zones.
When Expressions Aren’t Enough: The Code Node
Expressions are great for one-liners. But sometimes you find yourself writing nested ternary operators. Or, you might be trying to filter complex arrays inside the Expression Editor.
It is time to upgrade to the Code Node. This node allows you to write full multi-line JavaScript or Python.
Example: Deduplicating a Complex List
You can’t easily remove duplicates based on a specific sub-property using just expressions.
// Code Node Example
const seen = new Set();
return $input.all().filter(item => {
const duplicate = seen.has(item.json.email);
seen.add(item.json.email);
return !duplicate;
});
The “Limitless” Tier
This is where Thinkpeak.ai shines. Standard expressions cover most use cases. The remaining 20% involves complex business logic and API pagination loops.
Our Bespoke Engineering service handles this heavy lifting. We build entire backend architectures. If you need a Custom AI Agent capable of reasoning, we build the infrastructure to support it.
Debugging Your Expressions
Even the best architects face errors. Here is how to resolve the most common ones.
1. [Object object] Output
Cause: You are trying to print an entire object into a text field that expects a string.
Fix: Access the specific field like $json.name. Alternatively, you can stringify the object.
2. undefined
Cause: The field you are asking for doesn’t exist in the data for that specific item.
Fix: Use the Optional Chaining operator (?.). For example, {{ $json.address?.zipcode || "No Zip" }} returns “No Zip” if the field is missing.
3. “Referenced Node is not ancestor”
Cause: You are trying to use $node to access data from a branch that isn’t connected.
Fix: Connect the nodes directly. You can also use a Merge node to bring the data streams together.
Conclusion
Mastering expressions and variables takes you from “tinkerer” to “automation architect.” You can force n8n to do exactly what you want.
You simply need to understand the Array of Objects structure. Leverage the power of Luxon for dates. Utilize JavaScript for logic.
At Thinkpeak.ai, we help businesses skip the learning curve. Check out our Automation Marketplace for pre-architected workflows.
If you need scale, partner with our Bespoke Engineering team. We build custom low-code apps that integrate your entire stack. Transform your business into a self-driving ecosystem today.
Explore the Thinkpeak.ai Automation Marketplace
Frequently Asked Questions (FAQ)
What is the difference between $json and $input.item?
$json is actually a shorthand alias for $input.item.json. They access the exact same data. $json is preferred for brevity. However, $input.item is useful if you also need to access the binary data associated with that item.
How do I access data from two nodes ago?
You use the $node selector. For example: {{ $node["Name of Node"].json.field }}. Note that the node name must be exact and case-sensitive. If the node name has spaces, use brackets and quotes.
Why does my date expression return an error?
n8n dates are often strings. You must convert them to a Luxon object before you can modify them. Use DateTime.fromISO() or DateTime.fromFormat() first.




