跳到正文
cccskills
免费在 GitHub 公开

n8n-expression-syntax

Write and troubleshoot n8n expressions, the formulas that pass and transform data between workflow steps. Covers field references, webhook inputs, dates, strings, and choosing between expressions and Code nodes for data transformations.

中文简介正在准备中。当前显示英语简介。

原文查看原文说明

Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, mapping data between nodes, or referencing webhook data in workflows. Use this skill whenever configuring node fields that reference data from previous nodes — expressions are how n8n passes data between nodes, and getting the syntax wrong is the most common source of workflow errors. Also use when asked whether a complex expression hurts performance.

集成工具Slackn8nPostgreSQL
查看安装方法

适用场景

  • Fixing expression syntax errors
  • Using webhook data in downstream steps
  • Formatting dates and strings
  • Choosing expressions or Code nodes

包含的文件(4)

  • SKILL.md16.2 KB
  • COMMON_MISTAKES.md9.4 KB
  • EXAMPLES.md8.1 KB
  • README.md2.2 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

n8n Expression Syntax

Expert guide for writing correct n8n expressions in workflows.


Expression Format

All dynamic content in n8n uses double curly braces:

{{expression}}

Examples:

✅ {{$json.email}}
✅ {{$json.body.name}}
✅ {{$node["HTTP Request"].json.data}}
❌ $json.email  (no braces - treated as literal text)
❌ {$json.email}  (single braces - invalid)

Core Variables

$json - Current Node Output

Access data from the current node:

{{$json.fieldName}}
{{$json['field with spaces']}}
{{$json.nested.property}}
{{$json.items[0].name}}

$node - Reference Other Nodes

Access data from any previous node:

{{$node["Node Name"].json.fieldName}}
{{$node["HTTP Request"].json.data}}
{{$node["Webhook"].json.body.email}}

Important:

  • Node names must be in quotes
  • Node names are case-sensitive
  • Must match exact node name from workflow

$now - Current Timestamp

Access current date/time:

{{$now}}
{{$now.toFormat('yyyy-MM-dd')}}
{{$now.toFormat('HH:mm:ss')}}
{{$now.plus({days: 7})}}

$env - Environment Variables

Access environment variables:

{{$env.API_KEY}}
{{$env.DATABASE_URL}}

Warning: Some n8n instances have N8N_BLOCK_ENV_ACCESS_IN_NODE enabled, which blocks $env access entirely. If $env returns errors, use alternative approaches:

  • Store values in credentials instead
  • Use a Set node with manually entered values
  • Pass values through webhook query parameters

🚨 CRITICAL: Webhook Data Structure

Most Common Mistake: Webhook data is NOT at the root!

Webhook Node Output Structure

{
  "headers": {...},
  "params": {...},
  "query": {...},
  "body": {           // ⚠️ USER DATA IS HERE!
    "name": "John",
    "email": "john@example.com",
    "message": "Hello"
  }
}

Correct Webhook Data Access

❌ WRONG: {{$json.name}}
❌ WRONG: {{$json.email}}

✅ CORRECT: {{$json.body.name}}
✅ CORRECT: {{$json.body.email}}
✅ CORRECT: {{$json.body.message}}

Why: Webhook node wraps incoming data under .body property to preserve headers, params, and query parameters.


Common Patterns

Access Nested Fields

// Simple nesting
{{$json.user.email}}

// Array access
{{$json.data[0].name}}
{{$json.items[0].id}}

// Bracket notation for spaces
{{$json['field name']}}
{{$json['user data']['first name']}}

Reference Other Nodes

// Node without spaces
{{$node["Set"].json.value}}

// Node with spaces (common!)
{{$node["HTTP Request"].json.data}}
{{$node["Respond to Webhook"].json.message}}

// Webhook node
{{$node["Webhook"].json.body.email}}

Combine Variables

// Concatenation (automatic)
Hello {{$json.body.name}}!

// In URLs
https://api.example.com/users/{{$json.body.user_id}}

// In object properties
{
  "name": "={{$json.body.name}}",
  "email": "={{$json.body.email}}"
}

When NOT to Use Expressions

❌ Code Nodes

Code nodes use direct JavaScript access, NOT expressions!

// ❌ WRONG in Code node
const email = '={{$json.email}}';
const name = '{{$json.body.name}}';

// ✅ CORRECT in Code node
const email = $json.email;
const name = $json.body.name;

// Or using Code node API
const email = $input.item.json.email;
const allItems = $input.all();

❌ Webhook Paths

// ❌ WRONG
path: "{{$json.user_id}}/webhook"

// ✅ CORRECT
path: "user-webhook"  // Static paths only

❌ Credential Fields

// ❌ WRONG
apiKey: "={{$env.API_KEY}}"

// ✅ CORRECT
Use n8n credential system, not expressions

The transform gatekeeper

Before you add any node — or write any code — to transform data, walk this order and stop at the first that fits:

  1. Expression ({{ ... }}) in the consuming field. Property access, method chains (.map().filter().join()), ternaries, string building, Luxon date math — if it's "take A, produce B" without intermediate variables, it's an expression. This covers most "just transform this" cases.

  2. Arrow-function IIFE inside an Edit Fields field. When the logic needs intermediate variables, branching, or comments but still operates on one item, wrap it in an immediately-invoked arrow function right in the field value:

    ={{ (() => {
        const items = $json.line_items;
        const subtotal = items.reduce((sum, it) => sum + it.price * it.qty, 0);
        const tax = subtotal * 0.08;
        return (subtotal + tax).toFixed(2);
    })() }}
    

    The outer (...) brackets the function; the trailing () invokes it. Drop either and n8n refuses to run. Inside you get the full expression scope ($json, $('Node'), $now, Luxon) plus const/let, if/switch, try/catch, and regex. No require, no await.

  3. Code node — last resort. Only when you need multi-item aggregation across the whole dataset ($input.all()), an allowlisted library, or async work.

Why the order matters. It's not style — it's readability and performance. The Code node runs in a sandboxed VM with per-invocation setup and value marshaling — a cold-start cost that can reach 500–1000ms before your logic runs. (It amortizes on warm, high-item-count runs, so treat this as the common-case cost, not a universal constant.) The same logic in an expression or Edit Fields IIFE runs in-process in single-digit milliseconds and skips the sandbox entirely. For pure single-item shaping that's a large gap with no functional difference, and it compounds on hot paths like per-request webhooks. The expression also stays visible in the field that uses it, instead of hiding in an upstream node someone has to open to understand. Reach past a stage only when the input or scope genuinely demands it.

The Set-node antipattern and branch convergence

Delete Set nodes that feed one consumer

A Set / Edit Fields node whose only job is to extract a value and hand it to one downstream node is dead weight. Inline its expression at the consumer instead.

❌  Webhook → Set { customer_id: {{ $json.body.customer_id }} } → Postgres: WHERE id = {{ $json.customer_id }}

✅  Webhook → Postgres: WHERE id = {{ $('Webhook').item.json.body.customer_id }}

The Set node adds a hop, more canvas clutter, and a refactor hazard, while doing nothing the consumer couldn't do itself. To remove it cleanly with n8n_update_partial_workflow: rewire the connection (removeConnection from the Set's source-and-target, addConnection straight from source to consumer), patchNodeField the consumer's expression to reference the original source by node name, then removeNode the Set.

Quick test: count how many downstream nodes reference each field the Set produces.

  • 0 or 1 → delete, inline at the consumer.
  • 2+ → it may earn its place.

Legitimate exceptions — keep the Set when:

  • 2+ consumers read the same derived value and the derivation is non-trivial (a name aids readability and you compute it once).
  • It's a sub-workflow's final Return node, shaping the output contract. Here the "single consumer" is every caller, so the Set is the API boundary — and with Include Other Fields: false it whitelists the output shape so internal scratch fields don't leak.
  • You're renaming or whitelisting fields and want that visible in one place rather than spread across consumer expressions.

Branch convergence: anchor with a NoOp

When branches converge (after IF/Switch/Merge), $json becomes "whichever branch fired last" — non-deterministic, and a silent source of wrong data. Insert a NoOp node at the convergence, name it descriptively (Combine Inputs), and have downstream nodes reference it by name:

Branch A ──┐
           ├─→ [NoOp: Combine Inputs] ──→ downstream uses $('Combine Inputs').item.json.x
Branch B ──┘

The NoOp survives refactors: inserting a transform later between it and the consumer doesn't break the $('Combine Inputs') reference. (If the branches produce different shapes, use a Set node instead of a NoOp to normalize both into one shape — see the exceptions above.)

More broadly in branchy flows, prefer $('Node').item.json.x over deep $json.x. $json breaks the moment an intermediate node is inserted or a node clears item context (Aggregate, Code with Run for All, branching merges); the failure is silent and downstream gets the wrong data with no error. A node-name reference is unambiguous regardless of what sits between source and consumer.


Validation Rules

1. Always Use {{}}

Expressions must be wrapped in double curly braces.

❌ $json.field
✅ {{$json.field}}

2. Use Quotes for Spaces and Special Characters

Field or node names with spaces, diacritics, or special characters require bracket notation:

❌ {{$json.field name}}
✅ {{$json['field name']}}

❌ {{$node.HTTP Request.json}}
✅ {{$node["HTTP Request"].json}}

// Bracket notation is mandatory for keys with special characters
✅ {{$json['Gross Price w/o shipment']}}
✅ {{$json['Cena brutto zł']}}

3. Match Exact Node Names

Node references are case-sensitive:

❌ {{$node["http request"].json}}  // lowercase
❌ {{$node["Http Request"].json}}  // wrong case
✅ {{$node["HTTP Request"].json}}  // exact match

4. No Nested {{}}

Don't double-wrap expressions:

❌ {{{$json.field}}}
✅ {{$json.field}}

Common Mistakes

For complete error catalog with fixes, see COMMON_MISTAKES.md

Quick Fixes

MistakeFix
$json.field{{$json.field}}
{{$json.field name}}{{$json['field name']}}
{{$node.HTTP Request}}{{$node["HTTP Request"]}}
{{{$json.field}}}{{$json.field}}
{{$json.name}} (webhook){{$json.body.name}}
'={{$json.email}}' (Code node)$json.email

Working Examples

For real workflow examples, see EXAMPLES.md

Example 1: Webhook to Slack

Webhook receives:

{
  "body": {
    "name": "John Doe",
    "email": "john@example.com",
    "message": "Hello!"
  }
}

In Slack node text field:

New form submission!

Name: {{$json.body.name}}
Email: {{$json.body.email}}
Message: {{$json.body.message}}

Example 2: HTTP Request to Email

HTTP Request returns:

{
  "data": {
    "items": [
      {"name": "Product 1", "price": 29.99}
    ]
  }
}

In Email node (reference HTTP Request):

Product: {{$node["HTTP Request"].json.data.items[0].name}}
Price: ${{$node["HTTP Request"].json.data.items[0].price}}

Example 3: Format Timestamp

// Current date
{{$now.toFormat('yyyy-MM-dd')}}
// Result: 2025-10-20

// Time
{{$now.toFormat('HH:mm:ss')}}
// Result: 14:30:45

// Full datetime
{{$now.toFormat('yyyy-MM-dd HH:mm')}}
// Result: 2025-10-20 14:30

Data Type Handling

Arrays

// First item
{{$json.users[0].email}}

// Array length
{{$json.users.length}}

// Last item
{{$json.users[$json.users.length - 1].name}}

Objects

// Dot notation (no spaces)
{{$json.user.email}}

// Bracket notation (with spaces or dynamic)
{{$json['user data'].email}}

Strings

// Concatenation (automatic)
Hello {{$json.name}}!

// String methods
{{$json.email.toLowerCase()}}
{{$json.name.toUpperCase()}}

Numbers

// Direct use
{{$json.price}}

// Math operations
{{$json.price * 1.1}}  // Add 10%
{{$json.quantity + 5}}

Advanced Patterns

Conditional Content

// Ternary operator
{{$json.status === 'active' ? 'Active User' : 'Inactive User'}}

// Default values
{{$json.email || 'no-email@example.com'}}

Date Manipulation

// Add days
{{$now.plus({days: 7}).toFormat('yyyy-MM-dd')}}

// Subtract hours
{{$now.minus({hours: 24}).toISO()}}

// Set specific date
{{DateTime.fromISO('2025-12-25').toFormat('MMMM dd, yyyy')}}

String Manipulation

// Substring
{{$json.email.substring(0, 5)}}

// Replace
{{$json.message.replace('old', 'new')}}

// Split and join
{{$json.tags.split(',').join(', ')}}

Performance: expression complexity is (almost) free

A common worry is that a complex {{ }} is slow. It isn't — what costs is how many times n8n evaluates an expression, not how elaborate each one is.

Measured on an n8n 2.x instance, an elaborate expression (sqrt, split, reduce, arithmetic) costs the same per item as a trivial {{ $json.x > 50 }} — roughly ~0.2 ms/item either way, because ~90% of that is n8n building the per-item evaluation context, not running your expression.

What this means in practice:

  • Don't break a working expression into a chain of nodes for "speed." Each extra node re-evaluates per item and re-copies all items; one node with one richer expression beats three nodes with simple ones.
  • An expression (~0.2 ms/item) is ~3× cheaper than a Code node in "Run Once for Each Item" mode (~0.6 ms/item) for the same per-item check — but a Code node in "Run Once for All Items" mode is cheaper still (~0.02 ms/item), because it crosses the per-item boundary once instead of N times.
  • This only bites at thousands of items; below that it's sub-100 ms. The n8n Code JavaScript skill has the full per-item-boundary model.

Debugging Expressions

Test in Expression Editor

  1. Click field with expression
  2. Open expression editor (click "fx" icon)
  3. See live preview of result
  4. Check for errors highlighted in red

Common Error Messages

"Cannot read property 'X' of undefined" → Parent object doesn't exist → Check your data path

"X is not a function" → Trying to call method on non-function → Check variable type

Expression shows as literal text → Missing {{ }} → Add curly braces


Expression Helpers

Available Methods

String:

  • .toLowerCase(), .toUpperCase()
  • .trim(), .replace(), .substring()
  • .split(), .includes()

Array:

  • .length, .map(), .filter()
  • .find(), .join(), .slice()

DateTime (Luxon):

  • .toFormat(), .toISO(), .toLocal()
  • .plus(), .minus(), .set()

Number:

  • .toFixed(), .toString()
  • Math operations: +, -, *, /, %

Best Practices

✅ Do

  • Always use {{ }} for dynamic content
  • Use bracket notation for field names with spaces
  • Reference webhook data from .body
  • Use $node for data from other nodes
  • Test expressions in expression editor

❌ Don't

  • Don't use expressions in Code nodes
  • Don't forget quotes around node names with spaces
  • Don't double-wrap with extra {{ }}
  • Don't assume webhook data is at root (it's under .body!)
  • Don't use expressions in webhook paths or credentials

Related Skills

  • n8n MCP Tools Expert: Learn how to validate expressions using MCP tools
  • n8n Workflow Patterns: See expressions in real workflow examples
  • n8n Node Configuration: Understand when expressions are needed

Summary

Essential Rules:

  1. Wrap expressions in {{ }}
  2. Webhook data is under .body
  3. No {{ }} in Code nodes
  4. Quote node names with spaces
  5. Node names are case-sensitive

Most Common Mistakes:

  • Missing {{ }} → Add braces
  • {{$json.name}} in webhooks → Use {{$json.body.name}}
  • {{$json.email}} in Code → Use $json.email
  • {{$node.HTTP Request}} → Use {{$node["HTTP Request"]}}

For more details, see:


Need Help? Reference the n8n expression documentation or use n8n-mcp validation tools to check your expressions.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Design AI agents and chatbots in n8n, choosing suitable nodes and connecting models, tools, conversation memory, and structured JSON output. Includes guidance on document retrieval and human approval before tool execution.

中文简介正在准备中。当前显示英语简介。

  • Building a chatbot with conversation memory
  • Connecting external tools to an AI agent
  • Returning data in a defined JSON format
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

Build n8n workflows that download, transform, and send files without losing their contents. Learn how file data differs from JSON, how to preserve attachments, and how to use storage keys or URLs with AI tools and chat messages.

中文简介正在准备中。当前显示英语简介。

  • Fixing attachments lost during data transformation
  • Passing uploaded files to AI agent tools
  • Displaying generated images in chat
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

Write JavaScript for n8n Code nodes to aggregate, filter, and transform workflow data. Guidance covers data access, output formats, execution modes, common errors, and performance choices when processing large numbers of records.

中文简介正在准备中。当前显示英语简介。

  • Aggregating and deduplicating multiple records
  • Transforming complex API responses
  • Fixing data access and return format errors
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

Write Python for n8n Code nodes to transform, filter, aggregate, and validate workflow data. Get guidance on reading input items, returning the required JSON structure, using standard library modules, and avoiding common errors within n8n's Python execution modes.

中文简介正在准备中。当前显示英语简介。

  • Transforming and filtering data in n8n with Python
  • Calculating totals and basic statistics
  • Extracting and validating text with regular expressions
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

Build custom calculation and parsing tools that an n8n AI Agent can call. This skill covers JavaScript or Python inputs, structured arguments, string results, tool descriptions, and fixes for invocation and output errors.

中文简介正在准备中。当前显示英语简介。

  • Adding a calculation tool to an AI Agent
  • Defining typed inputs for multiple arguments
  • Fixing tool output type errors
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

Add error handling to n8n workflows with connected error outputs, retries, and failure notifications. Return HTTP status codes that explain what went wrong, distinguish invalid requests from service failures, and keep internal error details out of public responses.

中文简介正在准备中。当前显示英语简介。

  • Investigating workflows that fail without notification
  • Returning clear errors from webhook APIs
  • Retrying temporary network failures
czlonkowski/n8n-mcp2.3万2026年10月18日 更新

查看 czlonkowski 的全部技能

报告此技能的问题