n8n expressions use double curly braces {{ }} to dynamically reference and transform data inside node parameters. Access the current item with {{ $json.fieldName }}, reference other nodes with {{ $('NodeName').first().json.field }}, and use Luxon for dates and JMESPath for complex JSON queries. Expressions evaluate at runtime and let you build dynamic, data-driven workflows.
How to Use Expressions in n8n
Expressions are the backbone of dynamic data handling in n8n. They let you reference data from previous nodes, transform values, perform calculations, and build strings dynamically — all within node parameter fields. Instead of hardcoding values, expressions pull data at runtime from the workflow execution context, making your workflows flexible and reusable.
Prerequisites
- A running n8n instance with the workflow editor open
- At least one workflow with nodes that produce output data
- Basic understanding of JSON data structures
Step-by-step guide
Open the expression editor for a parameter
Open the expression editor for a parameter
Most node parameters support expressions. To switch a parameter from fixed value mode to expression mode, click the expression toggle button (the curly braces icon or the dropdown selector) next to the parameter field. Once in expression mode, the field turns into an expression editor where you can type {{ }} expressions. The editor shows a live preview of the evaluated result based on the last execution data.
Expected result: The parameter field switches to expression mode with a different background color and shows a preview of the expression result below the field.
Reference data from the current item
Reference data from the current item
The most common expression pattern is referencing fields from the current input item. Use {{ $json.fieldName }} to access any field from the incoming JSON data. For nested objects, chain the properties: {{ $json.user.email }}. For arrays, use bracket notation: {{ $json.items[0].name }}. The $json variable always refers to the json property of the current item being processed.
1// Access a top-level field2{{ $json.name }}3// Output: "Alice"45// Access a nested field6{{ $json.address.city }}7// Output: "New York"89// Access an array element10{{ $json.tags[0] }}11// Output: "important"1213// Access the full item including metadata14{{ $json }}15// Output: the entire JSON objectExpected result: The expression preview shows the resolved value from the input data. The field dynamically pulls the correct value during execution.
Reference data from other nodes in the workflow
Reference data from other nodes in the workflow
To access output data from a specific node (not just the directly connected one), use the $() function with the node name. This is useful when you need data from a node earlier in the workflow that is not the immediate predecessor. Always use .first() or .last() to select a specific item, or .all() to get all items as an array.
1// Get a field from the first item of a specific node2{{ $('HTTP Request').first().json.status }}3// Output: 20045// Get a field from the last item of a specific node6{{ $('Webhook').last().json.body.email }}7// Output: "user@example.com"89// Get the number of items output by a node10{{ $('Fetch Orders').all().length }}11// Output: 151213// Access item at a specific index14{{ $('Code Node').all()[2].json.name }}15// Output: "Third Item"Expected result: The expression resolves to data from the specified node, regardless of where in the workflow it is located relative to the current node.
Use built-in date and time functions with Luxon
Use built-in date and time functions with Luxon
n8n includes the Luxon library for date and time manipulation. Use {{ $now }} for the current date and time as a Luxon DateTime object, and {{ $today }} for today's date at midnight. You can chain Luxon methods to format dates, add or subtract time, and compare timestamps.
1// Current date and time in ISO format2{{ $now.toISO() }}3// Output: "2026-03-27T14:30:00.000+00:00"45// Today's date formatted6{{ $today.toFormat('yyyy-MM-dd') }}7// Output: "2026-03-27"89// Yesterday's date10{{ $now.minus({ days: 1 }).toFormat('yyyy-MM-dd') }}11// Output: "2026-03-26"1213// Add 2 hours to current time14{{ $now.plus({ hours: 2 }).toISO() }}1516// Parse a date string from input data17{{ DateTime.fromISO($json.createdAt).toFormat('dd/MM/yyyy') }}18// Output: "27/03/2026"1920// Check if a date is in the past21{{ DateTime.fromISO($json.expiresAt) < $now }}Expected result: Date expressions resolve to formatted date strings or boolean comparisons that you can use in node parameters and conditions.
Use JMESPath for complex JSON queries
Use JMESPath for complex JSON queries
For complex data extraction from nested JSON structures, n8n supports JMESPath expressions via the $jmespath() function. JMESPath is especially useful for filtering arrays, selecting specific fields from nested objects, and flattening data structures without writing custom code.
1// Extract all names from an array of objects2{{ $jmespath($json.users, '[*].name') }}3// Output: ["Alice", "Bob", "Carol"]45// Filter array items where status is active6{{ $jmespath($json.orders, '[?status==`active`]') }}7// Output: array of orders with status "active"89// Get the first matching item's email10{{ $jmespath($json.contacts, '[?role==`admin`] | [0].email') }}11// Output: "admin@example.com"1213// Flatten nested arrays14{{ $jmespath($json, 'departments[*].employees[*].name[]') }}15// Output: flat array of all employee names across departmentsExpected result: Complex JSON data is extracted and transformed according to your JMESPath query, returning the filtered or restructured result.
Combine expressions with string interpolation
Combine expressions with string interpolation
You can mix static text with expressions to build dynamic strings. Place expressions inside {{ }} within a regular text string. This is commonly used for composing email bodies, API URLs, notification messages, and log entries that include dynamic data from the workflow.
1// Build a dynamic message2Hello {{ $json.name }}, your order #{{ $json.orderId }} has been shipped!34// Build a dynamic API URL5https://api.example.com/users/{{ $json.userId }}/orders?status={{ $json.status }}67// Multi-expression string8Processed {{ $('Fetch Data').all().length }} items on {{ $now.toFormat('yyyy-MM-dd') }}Expected result: The final string contains the resolved values from all expressions, creating a complete dynamic message or URL.
Complete working example
1// Code Node: Demonstrates n8n expression equivalents in JavaScript2// This shows how expression concepts map to Code node logic3// when you need more complex transformations.45const items = $input.all();6const results = [];78for (const item of items) {9 const data = item.json;10 11 // Equivalent of {{ $json.fieldName }}12 const customerName = data.name;13 14 // Equivalent of {{ $('Other Node').first().json.field }}15 const webhookData = $('Webhook').first().json;16 17 // Equivalent of {{ $now.toFormat('yyyy-MM-dd') }}18 const today = new Date().toISOString().split('T')[0];19 20 // String interpolation equivalent21 const message = `Hello ${customerName}, your request from ${today} is being processed.`;22 23 // Array filtering (JMESPath equivalent)24 const activeItems = (data.items || []).filter(25 item => item.status === 'active'26 );27 28 // Nested access with fallback29 const email = data?.contact?.email || 'no-email@example.com';30 31 results.push({32 json: {33 customerName,34 email,35 message,36 activeItemCount: activeItems.length,37 activeItems,38 processedAt: new Date().toISOString(),39 source: webhookData?.source || 'unknown'40 }41 });42}4344return results;Common mistakes when using Expressions in n8n to Reference Dynamic Data
Why it's a problem: Typing {{ $json.field }} in fixed value mode instead of switching to expression mode
How to avoid: Click the expression toggle next to the parameter field before typing your expression. In fixed mode, the curly braces are treated as literal text.
Why it's a problem: Using $('Node Name') without .first() or .all(), causing undefined errors
How to avoid: Always use .first().json, .last().json, or .all() after $('Node Name') to select specific items. The $() function returns a reference, not the data directly.
Why it's a problem: Using regular quotes instead of backticks for string literals in JMESPath filters
How to avoid: JMESPath uses backticks for string values inside filter expressions: [?status==`active`], not [?status=='active'].
Why it's a problem: Referencing a node by the wrong name after renaming it
How to avoid: The $() function uses the node's current display name. If you rename a node, update all expressions in other nodes that reference it.
Best practices
- Always use the expression editor preview to verify your expression resolves correctly before saving
- Use optional chaining in expressions like {{ $json.user?.email }} to avoid errors when fields might be missing
- Reference nodes by their exact display name — node names are case-sensitive in $() expressions
- Prefer $json for simple field access and $jmespath() for complex filtering and transformation
- Use Luxon methods for all date operations instead of JavaScript Date to ensure consistent timezone handling
- Keep expressions simple and readable — move complex logic to a Code node instead of writing long expressions
- Test expressions with different input data shapes to ensure they handle edge cases gracefully
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to write an n8n expression that gets the current date in YYYY-MM-DD format, extracts the email field from the first item of a node called 'Fetch Users', and combines them into a message string. Show me the expression syntax.
Write an expression for the HTTP Request node URL parameter that dynamically inserts the user ID from the Webhook node input and appends today's date as a query parameter.
Frequently asked questions
What is the difference between {{ $json }} and {{ $input }}?
$json is a shortcut for the json property of the current input item. $input gives you access to the full input object including metadata. For most cases, $json is what you need to reference field values.
Can I use JavaScript functions inside expressions?
Yes. You can use standard JavaScript methods like .toUpperCase(), .split(), .map(), Math.round(), and others inside expression brackets. For example, {{ $json.name.toUpperCase() }} converts the name to uppercase.
How do I handle null or undefined values in expressions?
Use optional chaining with the question mark operator: {{ $json.user?.email }}. You can also provide a fallback with the OR operator: {{ $json.email || 'no-email' }}.
Can expressions access environment variables?
Yes. Use {{ $env.VARIABLE_NAME }} to access environment variables that are set in your n8n configuration. This is useful for referencing API base URLs or other configuration values.
Why does my expression show [Object object] instead of the value?
This happens when the expression returns an object or array instead of a primitive value. Drill deeper into the object with additional property access, like {{ $json.result.data.value }} instead of {{ $json.result }}.
How do I debug expressions that return unexpected results?
Use the expression editor preview, which shows the evaluated result based on the most recent execution data. You can also add a Code node to log intermediate values and inspect the data structure.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation