
Firstly, if you find it troublesome to build your own system, you can easily use the official solution .Just Click here
In the process of practicing n8n, Xiangyu deeply realized the crucial role of “data mapping” in the success of workflows – it is like a blood vessel, efficiently and precisely delivering the “data blood” between various nodes. This tutorial is a guide specially created by Xiangyu for novices with no foundation. It starts with the most basic concepts of Fixed (fixed value) and Expression (expression), gradually analyzes various mapping techniques, practical cases, and common pitfalls, and compares them with the mapping mechanism of Make.com at appropriate times.
By reading this tutorial, Xiangyu hopes that all of you can not only quickly get started with the n8n interface but also draw on Xiangyu’s practical experience to confidently build efficient and easy-to-maintain automated workflows. Of course, due to the limitations of Xiangyu’s practical experience, there may be some omissions or inaccuracies in this tutorial. We sincerely ask for your understanding and welcome your corrections and additions, so that we can all make progress together.
Preface: Why is it necessary to understand data mapping?
Imagine you are playing a game of message passing, where you need to accurately convey the message from Student A to Student B. In the automation tool n8n, “data mapping” plays a similar role.
n8n can help you connect different apps or services and automate some repetitive tasks (such as automatically saving attachments to cloud storage upon receiving emails). It is very flexible, allowing you to design processes by dragging and dropping modules (called “nodes”) like building blocks, or write simple code to achieve complex functions.
In this process, “data mapping” is the core, which determines how the data processed by the previous node is accurately passed to the next node for use. For example, if you receive a name filled in by a user from a website form, and you want to automatically fill this name in the welcome email when sending it in the next step, data mapping is needed.
If the mapping is incorrect, it’s akin to miscommunicating a message, potentially disrupting the entire automation process, leading to data loss or producing strange results. Hence, even if you’re a novice in n8n, comprehending data mapping is a crucial initial step.
There are primarily two methods to set node parameters (which can be understood as configuration items for nodes) in n8n:
- Fixed (fixed value): directly set a fixed value.
- Expression:A special “formula” used to dynamically fetch values from other places.
This tutorial will guide you through these two methods in the simplest and most straightforward way, enabling you to easily grasp the data flow of n8n.

Core mapping method: Fixed (fixed value) vs. Expression (expression)
In the configuration interface of the n8n node, you will often see a small button next to the parameter that allows you to toggle between “Fixed” and “Expression”. Understanding the difference between the two is crucial:
1 Fixed (fixed value)
- What is it: Fill in a fixed and unchangeable value directly in the input box.
- When to use: When the value of this parameter should remain the same every time the process runs.
For example:
- You need to set up a “Send Mail” node, where the recipient address is always the same email of your boss.In this case, select the “Fixed” mode in the “Recipient” field and directly fill in your boss’s email address, boss@example.com.
- When connecting to a database, the database address and username are usually fixed.
- Advantages: Simple and straightforward, easy to understand, and less prone to errors.
- Disadvantages: Lack of flexibility, unable to adapt to changes in previous data.
1 Expression (expression)
- What it is: A “code snippet” or “formula” enclosed within a pair of double curly braces {{ }} for dynamically calculating the value of parameters.
- When to use: When the value of the parameter needs tobe determined based on the output data of preceding nodesor other dynamic information.
For example:
- Still in the “Send Email” node, but this time you want to address the recipient’s name in the email body. This name is obtained from the “Webhook Trigger” (such as receiving a form submission) in the previous step. At this time, the “Email Body” field should use the “Expression” mode, writing something like “Hello, {{
The expression is like this: $node(“Webhook”).json.body.name }}! The {{ … }} part here will be automatically replaced with the actual name received by the Webhook.
- Advantages: Highly flexible, enabling workflows to respond based on real-time data.
- Disadvantages: You need to learn some grammar rules (don’t worry, it’s quite simple).Summary:
- For fixed and unchanging values, use “Fixed”.
- For values that need to change dynamically (derived from data from other nodes), use an Expression.
Deeply understand Expression
Alright, since Expression is so important and flexible, let’s delve into several common usages of it. Remember, they are all enclosed within {{ }}.
1 Node expression: Reference data from other nodes
- Function: This is the most commonly used to obtaindata from the output results of a specific node
- Writing method: The basic format is {{ $(“node name”).item.json.field name }} or {{ $node(“node name”).json.field name }} (old writing method, the difference will be explained below).
- “$(node_name)” or “$node(node_name)”: Specify the node from which you want to obtain data, replacing “node_name” with the actual node name in your workflow (pay attention to capitalization).
- .item (new notation): This is where n8n shows its intelligence. It will attempt to find data that is currently being processed
The source data for “matching”.
- “.json”: indicates that you want to obtain the JSON-formatted data output by that node (n8n’s internal data is mainly in this format)
JSON).
- Field name: The specific information you want to extract from JSON data can be searched layer by layer using the dot notation or the syntax of [“field name”].
- Real data example: Suppose you have an HTTP Request named “Fetch User Info”
The node, after requesting the API, returned the following JSON data:
// Data output by the “Get User Information” node (Item 0) [{ “id”: 123,
“name”: “Zhang San”,
“email”: “[email protected]”, “address”: {
“street”: “No. 100, Renmin Road”,
“city”: “Shanghai”
}}]
json
Now, you want to use this user’s name and email in the content of the next “Send Message” node. You can write it like this in the “Message Content” field (using the Expression mode):
Username: {{$(“Get User Information”). tem. json. name}}, Email: {{$(“Get User Information”). tem. json. email}}
handlebars
When the workflow reaches this step, the expression above will be replaced with actual values, and the final message content sent will be:
User name: Zhang San, email: [email protected]
actionscript
If you want to obtain nested city information, you can write like this:
{{$(“Get User Information”). item. json. address. city}}
handlebars
The result is: Shanghai
Notes:
- The node name must be correct: it must be exactly the same as the name displayed in your workflow. If you modify the node name, the expression should also be changed accordingly.
- The .json extension cannot be omitted: typically, it is necessary to add the .json extension to access the data within.
- Data pairing (.item): The new version of n8n (recommended to use $(“…”)) intelligently matches data. If it cannot determine the data source (such as after merging or splitting), it may report an error (how to solve it will be discussed later).
- Execution order: Only data fromnodes that have already been executedcan be referenced.
New and old grammar:
- Recommendation: “NodeName”.item.json… (Utilizing Item Linking intelligent matching)
- Legacy/Compatible: $node(“node_name”).json… (It’s more like fetching in the order of data items, and may cause errors if the data volume doesn’t match.) Although it can still be used, it’s recommended to use the syntax $(“…”) for new projects. If you see older tutorials online that use the older syntax $node[“node_name”], you should also switch to the new one.
1 Item expression: specifies the item number to retrieve data from
- Function: Sometimes, a node may outputa list(multiple pieces of data), and you only want to explicitly obtain a specific one from it (such as always obtaining the first one), or you want to reference another piece of data with a fixed index when processing a certain piece of data.
- Writing method: Start with $item(index). The index starts from 0 (0 represents the first item, 1 represents the second item, and so on).
- {{ $item(0).$node(“node_name”).json.field }}: This is used to forcefully obtain a specific field from thefirstpiece of data output by the “node_name”.
Application Scenario:
- Shared configuration: One node obtains an API key (with only one piece of data), while subsequent nodes need to process a list of users (with multiple pieces of data), and each processing requires the same API key.
Handling specific positions in an array: A node returned [“apple”, “banana”, “orange”], and you want to handle them separately in the next step.
- Real data example:
- Read configuration node output (only one piece of data, with index 0):
json
[{
“apiKey”: “mysecretkey12345”,
“apiUrl”: “[https://api.example.com](https://api.example.com)”}]
- Input to the “Process Order” node (there are multiple order data, for example, the 2nd order is being processed, with index 1):
json [{“orderId”: “A001”, “amount”: 100},{“orderId”: “A002”, “amount”: 200}, // <– Currently processing this order with orderId “A002”
- In the “Process Order” node, an API needs to be called for each order, and the API Key should always be the one output by “Read Configuration”. The expression should be written as:
{{$item (0). $node (“read configuration”). json. apiKey}}
handlebars
Even if we are currently processing the second order (index 1), this expression will force the retrieval of the apiKey from the first (index 0) piece of data output by the “read config” node. The result will always be: “mysecretkey12345”.
- Notes:
- The index starts from 0.
- $item(index) should be placed at the forefront of the expression.
- The new version of n8n provides a more readable alternative method:
- “$(node_name).first().json…” (equivalent to $item(0))
- “$(selector).last().json” … (to get the last item)
- $(“Node Name”). all() [Index]. json… (Get an array of all data and then index it by index) It is recommended to prioritize using these new methods for clearer semantics.
- The old syntax of $item(“0”) (with quotation marks) is outdated and should not be used.
- JSONPath, JMESPath, and JavaScript expressions: Flexible data processing
The expression capabilities of n8n are extremely powerful. It can not only evaluate values but also filter, calculate, and transform data.
- JMESPath query ($jmespath):
- Function: It is particularly convenient when it comes to accurately selecting data from complex JSON structures, or filtering and transforming arrays. It is a language specifically designed for querying JSON.
- Syntax: {{ $jmespath(“query statement”, data source) }}
- Real data example: Assume that $json (the input data of the current node) is:
json
{“department”: “Sales”,”employees”: [
{“name”: “Alice”, “age”: 30, “active”: true},
{“name”: “Bob”, “age”: 25, “active”: false},
{“name”: “Charlie”, “age”: 35, “active”: true}]}
- Extract the names of all employees:
{{ $jmespath(“employees[*].name”, $json) }}
handlebars
Result: [“Alice”, “Bob”, “Charlie”] (an array of names)
- Extract only the names ofactive employees:
{{ $jmespath(“employees[?active==`true`].name”, $json) }}
handlebars
Result: [“Alice”, “Charlie”]
- Get the age of the first employee:
{{ $jmespath(“employees[0].age”, $json) }}
handlebars
Result: 30
- Built-in helper functions (similar to Lodash): n8n provides some convenient small functions.
- $ifEmpty(value, default_value): If the value is empty (null, undefined, ), return the default value.
- Example: {{ $ifEmpty($json.description, “No description provided”) }}
- If $json.description is an empty string “”, the result will be “No description available”.
- $if(condition, value_if_true, value_if_false): Similar to the IF function in Excel.
- Example: {{ $if($json.score >= 60, “Pass”, “Fail”) }}
- If $json.score is 75, the result is “Pass”.
- There are also $max(…), $min(…) and so on.
- $ifEmpty(value, default_value): If the value is empty (null, undefined, ), return the default value.
- Native JavaScript (JS): An expression is essentially executing JS code, so it can be directly used in JS
Grammar.
- String concatenation:
- Input data: {“firstName”: “Li”, “lastName”: “Lei”}
- Expression: {{ $json.firstName + + $json.lastName }}
- Result: “Li Lei”
- Mathematical calculation:
- Input data: {“price”: 19.9, “quantity”: 3}
- Expression: {{ $json.price * $json.quantity }}
- Result: 59.7
- Calling JS method:
- Input data: {“message”: “hello world”}
- Expression: {{ $json.message.trim().toUpperCase() }} (remove leading and trailing spaces and convert to uppercase)
- Result: “HELLO WORLD”
- How to choose?
- Simple value retrieval, addition, subtraction, multiplication, and division, as well as string processing: directly use JavaScript.
- Need to make judgments and assign default values: Using $if and $ifEmpty is clearer.
- Filtering/extracting from complex JSON or arrays: Prioritize $jmespath.
- The logic is too complex (such as multiple lines of code): Don’t cram it into an expression. Consider using a dedicated Function (Code) node to write JS code, which is easier to maintain.
- Variable and parameter mapping: not just node data
Expressions can not only obtain node data, but also access some global information and special variables.
- Workflow and execution information:
- $workflow.name: The name of the current workflow.
- $execution.id: The unique ID of this execution.
- Purpose: This information can be included when sending notifications or logging, facilitating the traceability of which process and which run generated it.
- Example: The workflow [{{ $workflow.name }}] has been executed, ID: {{ $execution.id }}
- Environment variables ($env):
- You can obtain the variables set for the n8n runtime environment.
- Syntax: {{ $env(“variable_name”) }}
- Purpose: To store sensitive information such as API keys, which are not directly hardcoded in the workflow, ensuring greater security.
- Example: {{ $env(“MY_SECRET_API_KEY”) }}
- Global variables ($vars):
- You can define some globally applicable variables in the n8n settings.
- Writing method: {{ $vars.variable_name }}
- Purpose: To store values that may be used across multiple workflows, such as company names and generic email addresses.
- Example: {{ $vars.companyName }}
- Date and time ($now, $today):
- $now: Gets the current complete date and time (it is a special object that can be called with methods).
- $today: Only retrieves the current date (with the time part set to zero).
- Common operations:
- {{ $now.toISO() }}: Outputs a standard ISO format time string, such as “2025-05-03T14:26:56.000Z”.
- {{ $now.toFormat(“yyyy-MM-dd HH:mm:ss”) }}: Output in the specified format, such as “2025-05-03 07:26:56” (note the time zone).
- {{ $now.plus({days: 1}).toISODate() }}: Gets the date of tomorrow, such as “2025-05-04”.
- Special node variables:
- HTTP Request node: If automatic pagination (Pagination) is enabled, you can
Use $http.pagination to obtain pagination information, such as setting the request URL for the next page using {{ $http.pagination.nextPage }}.
- Function (Code) node: When writing JS code in this node, you can directly
When using variables like $json, $node(…), there is no need to wrap them with {{ }} outside. This is a special feature of the Function node.
- Community plugins and custom expressions
n8n boasts an active community that has contributed numerous additional nodes (community plugins).
- Using community plugins: These plugins provide more functionality, but the way you obtain data from them is still to use the Node expression we mentioned above, such as {$(“community plugin node name”). item.json. output fields}}.
- Compatibility issue: Some old community plugins may not be fully compatible with n8n’s latest data linking (Item Linking)
Mechanism. If you get data using $(“plugin name”). tem… and receive an error saying “no match found”, you can try:
- Use $(“plugin name”). first(). json… (only take the first one)
- Use $(“plugin name”). all() [0]. json… (Retrieve the first entry by index)
- Contact the plugin author for an update.
- Custom expressions: n8n does not support users adding arbitrary global custom functions to expressions. For complex custom logic, it is recommended to implement it in the Function node.
Common errors and solutions
When writing expressions, it is inevitable to encounter some problems. Here are a few common errors and their solutions:
- Error message: “Referenced node is unexecuted” / “Can’t get data for expression”
- Meaning: The node you want to reference has not been executed in the current process path, so its data cannot be obtained.
- Processing method:
- Check the order of node connections to ensure that the referenced nodes are indeed executed first.
- If it is caused by an IF/Switch branch, either ensure that the referenced node is executed in all branches, or use $if or $(“node_name”).isExecuted (to determine whether the node has been executed) to handle situations where data may not exist.
- It is sometimes normal to see this error when previewing in the editor, as it hasn’t been executed yet. Try executing the workflow and check again.
- Error message: “Multiple matching items for expression” / “Can’t determine which item to use”
- Meaning: You used .item to obtain data, but n8n found that the current data may correspond to multiple pieces of data from the source node, and it doesn’t know which one to use. This is commonly seen after data has been merged (Merge), looped (Loop), or aggregated (Aggregate).
- Processing method:
- Most commonly used: Stop using .item and explicitly specify which one you want. Instead, use:
1 .first(): Gets the first item.
2 .last(): Gets the last item.
3 .all()[index]: Obtain the array composed of all data, and then specify the index to retrieve.
- Check the workflow logic to see if it is possible to avoid ambiguity in data sources.
- Error message: “The ‘JSON Output’ in item X contains invalid JSON”
- Meaning: You may be in a field that requires filling in a standard JSON format (such as the JSON of a Set node)
In the mode, the expression result filled in is not a valid JSON.
- Handling method:
- Ensure that your expression generates complete and well-formatted JSON.
- Note that string values need to be enclosed in quotation marks. If the expression result is an object, it may need to be converted using JSON.stringify().
- Check the generated content using a JSON validation tool.
- Error message: “Invalid syntax”
- Meaning: There are errors in your expression syntax, such as spelling mistakes, incorrect use of symbols, unmatched parentheses, etc.
- Processing method:
- Carefully check the content inside {{ }}. n8n’s expression editor usually highlights errors in red.
- Compare the example in the tutorial to see if there is any formatting error.
- Complex expressions can be broken down into several parts for testing.
Troubleshooting tips:
- Make good use of the node output previewfunction of n8n to see what kind of data structure is actually output by each node.
- Write simple expressions first, and gradually add more complex logic after testing has been successful.
- When in doubt, refer to the official n8n documentation (Expressions section).
Evolution of expression function versions (for understanding only)
n8n has been evolving, and its expression functionality has undergone some changes. Understanding these can help you comprehend old tutorials or handle old workflows.
- Key change: Item Linking (pairing items)
- It was introduced around version 0.2xx, and version 1.0 became the core.
- The goal is to ensure that data can still accurately match its source even in complex processes, such as branch merging.
- A new recommendation syntax has been introduced: “node name” paired with .item, .first(), .last(), and .all().
- Old syntax (not recommended):
- $node(“node_name”) or $node[“node_name”]: Matching by index is prone to errors.
- $item(“0”) (quoted index): deprecated.
- .data attribute: Now it is uniformly used as .json.
- Strict error handling (v1.0+):
- Previously, if the expression was written incorrectly, it might just return a null value, and the process would continue to run.
- Now, an expression error will directly lead to node failure and process interruption (unless you have set up an error handling process). This is safer, but it also requires you to be more careful when writing expressions.
In summary, when writing new workflows, try to use new syntax such as $(“node_name”).first()… or $(“node_name”).all()[…], and avoid using square brackets $node[“…”] and quotation marks $item(“…”).
Appendix: Quick Reference for Common Expressions & FAQ
Quick Reference Table of Common Expressions
Frequently Asked Questions (FAQ)
- Q: When should I use an Expression node, and when should I use a Function node?
- Answer: For simple data passing and conversion (such as fetching a value, concatenating a string, or making a simple judgment), using Expression is more convenient and faster. If the logic is complex (such as requiring loops, multi-step calculations, calling external APIs, etc.), or if a piece of logic needs to be reused in multiple places, then writing JS code using the Function node will be clearer and easier to maintain.
- Q: What is the difference between $(“node”) and $node(“node”)? Which one should I use?
- Answer: “jQuery nodes” is a new syntax, and it’s recommended to use it!It has intelligent data matching logic behind it. Usually, it matches
Use .first(), .last(), .all(), or .item. $node(“node”) is the old syntax, which is more like directly accessing by the order (index) of data items, and it is prone to errors in complex processes. Although it can still be used, please use $(“…”) for new projects.
- Q: Can external JS libraries like Lodash or moment.js be used in expressions?
- Answer:It cannot be used directly.The expression environment of n8n is sandboxed, and only Luxon (for handling date and time, used through $now) and JMESPath (for handling JSON queries, used through $jmespath) as well as some auxiliary functions are built-in. If you need complex functionalities from libraries like Lodash, you should write code in the Function node (you may need to configure n8n to allow the introduction of external modules), and then output the result for use in subsequent nodes.
- Question: Why can’t I see the preview result in the expression editor sometimes?
- Answer: There are several possible reasons: 1) The referenced node has not been executed yet and has no data. 2) The expression relies on runtime data matching (such as using .item), and the editor cannot statically determine it. 3) There is an error in the expression itself. For the second case, even if the preview is empty, it usually works normally during actual runtime. The most reliable way is to check the actual execution result.
- Question: If a field may not exist, how should the expression be handled to avoid errors?
- Answer:You can use the default value technique:
- {{ $json.maybeField || “default value” }} (JavaScript’s OR operator)
- {{ $json.user?.address?.street || “Address unknown” }} (Optional chaining operator ?. in JS, recommended)
- {{ $ifEmpty($json.maybeField, “default value”) }} (n8n built-in function) It is recommended to use optional chaining ?. or $ifEmpty for clearer semantics.
- Q: What is the difference between data mapping in n8n and Make (formerly Integromat)?
- Answer:Make primarily employs point-and-click mapping, allowing users to drag and drop data “capsules” onto target fields on the interface. It also incorporates functions, but more akin to filling out a table. n8n primarily involves writing expressions {{ … }} , which is closer to coding.
- Advantages: Highly user-friendly and intuitive for non-technical personnel.
- Advantages of n8n: Extremely flexible, allowing almost any data manipulation with JS, and developer-friendly.
- Make’s disadvantage: Complex mapping may require nesting many modules or functions, which may not necessarily be simpler than n8n.
- Disadvantages of n8n: It requires learning a bit of expression syntax, which poses a barrier for those who are completely unfamiliar with coding. Overall, Make is more “visual”, while n8n is more “script-oriented”. If you need fine-grained control over data processing, n8n has stronger expression capabilities.
- Q: Can we dynamically decide which node’s data to reference? For example, use the output from node A or node B based on certain conditions?
- Answer:Make primarily employs point-and-click mapping, allowing users to drag and drop data “capsules” onto target fields on the interface. It also incorporates functions, but more akin to filling out a table. n8n primarily involves writing expressions {{ … }} , which is closer to coding.
- Answer:You can use the default value technique:
- Answer: You cannot directly use variables to dynamically specify node names within the parentheses of $(“…”) or $node(“…”)
If $($json.sourceNodeName) is used, it will not work. The node name must be a fixed string. To fulfill this requirement, the usual approach is:
- Use IF or Switch nodes to separate the process, handle them separately, and then merge them.
- In the Function node, use JS’s if/else statements to determine conditions, then retrieve values from different $node(“A”)… or $node(“B”)…, and finally output a unified result.
Question: Can an expression reference the data of a step node in the subsequent steps?
Answer: No. The data in n8n flows sequentially through the process. A node can only reference the data of nodes that have been executed before it, and it cannot “predict the future”. If your logic requires the results of subsequent steps, it may be necessary to redesign the order of the process.
Conclusion
So far, Xiangyu has led everyone to comprehensively sort out the data mapping system in n8n: the simplicity and intuition of fixed values, the flexibility and diversity of expressions, and the deep dissection from Node, Item to JMESPath, global variables; it also covers common error troubleshooting and version evolution context. After mastering these key points, readers will no longer be stalled by confusing field paths or indexes, but will be able to confidently handle various complex scenarios with a data-driven mindset, just like Xiangyu. May this ultra-detailed tutorial become a reliable reference for you on the road to n8n automation, helping more efficient and innovative workflows to be implemented!