feat(skills): Add n8n-automation collection (3 skill sets, 14 files)
- expression-syntax: {{}} expressions, , variables
- javascript-code: Code nodes, , , patterns
- node-configuration: Operation-aware setup, dependencies
- n8n runs on Command Center for Firefrost workflows
- Updated SKILLS-INDEX.md
Chronicler #73
This commit is contained in:
@@ -264,6 +264,30 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### n8n-automation (collection)
|
||||||
|
**Location:** `docs/skills/n8n-automation/`
|
||||||
|
**Source:** skill.fish
|
||||||
|
**Triggers:** n8n, workflow automation, expressions, Code node, JavaScript, node configuration
|
||||||
|
|
||||||
|
**Purpose:** n8n workflow automation guidance
|
||||||
|
|
||||||
|
**What It Covers:**
|
||||||
|
- **expression-syntax/** — {{}} expressions, $json, $node, $now variables
|
||||||
|
- **javascript-code/** — Code nodes, $input, $helpers, DateTime
|
||||||
|
- **node-configuration/** — Operation-aware node setup, property dependencies
|
||||||
|
|
||||||
|
**Firefrost Context:**
|
||||||
|
- n8n runs on Command Center (63.143.34.217)
|
||||||
|
- Used for: Subscription sync, server monitoring, notifications
|
||||||
|
|
||||||
|
**Read This When:**
|
||||||
|
- Writing n8n expressions
|
||||||
|
- Creating Code nodes with JavaScript
|
||||||
|
- Configuring complex nodes
|
||||||
|
- Troubleshooting n8n errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### postgres-patterns
|
### postgres-patterns
|
||||||
**Location:** `docs/skills/postgres-patterns/SKILL.md`
|
**Location:** `docs/skills/postgres-patterns/SKILL.md`
|
||||||
**Source:** skill.fish (affaan-m/everything-claude-code)
|
**Source:** skill.fish (affaan-m/everything-claude-code)
|
||||||
@@ -454,6 +478,22 @@ docs/skills/
|
|||||||
│ └── SKILL.md
|
│ └── SKILL.md
|
||||||
├── minecraft-mod-dev/
|
├── minecraft-mod-dev/
|
||||||
│ └── SKILL.md
|
│ └── SKILL.md
|
||||||
|
├── n8n-automation/
|
||||||
|
│ ├── README.md
|
||||||
|
│ ├── expression-syntax/
|
||||||
|
│ │ ├── SKILL.md
|
||||||
|
│ │ ├── EXAMPLES.md
|
||||||
|
│ │ └── COMMON_MISTAKES.md
|
||||||
|
│ ├── javascript-code/
|
||||||
|
│ │ ├── SKILL.md
|
||||||
|
│ │ ├── DATA_ACCESS.md
|
||||||
|
│ │ ├── BUILTIN_FUNCTIONS.md
|
||||||
|
│ │ ├── COMMON_PATTERNS.md
|
||||||
|
│ │ └── ERROR_PATTERNS.md
|
||||||
|
│ └── node-configuration/
|
||||||
|
│ ├── SKILL.md
|
||||||
|
│ ├── OPERATION_PATTERNS.md
|
||||||
|
│ └── DEPENDENCIES.md
|
||||||
├── postgres-patterns/
|
├── postgres-patterns/
|
||||||
│ └── SKILL.md
|
│ └── SKILL.md
|
||||||
├── social-automation/
|
├── social-automation/
|
||||||
|
|||||||
106
docs/skills/n8n-automation/README.md
Normal file
106
docs/skills/n8n-automation/README.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# n8n Automation Skills
|
||||||
|
|
||||||
|
Comprehensive n8n workflow automation guidance for Firefrost.
|
||||||
|
|
||||||
|
**n8n Location:** Command Center (63.143.34.217)
|
||||||
|
**Access:** https://n8n.firefrostgaming.com (or via Cloudflare tunnel)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Available Skills
|
||||||
|
|
||||||
|
| Skill | Folder | Purpose |
|
||||||
|
|-------|--------|---------|
|
||||||
|
| Expression Syntax | `expression-syntax/` | {{}} expressions, $json, $node variables |
|
||||||
|
| JavaScript Code | `javascript-code/` | Code nodes, $input, $helpers |
|
||||||
|
| Node Configuration | `node-configuration/` | Operation-aware node setup |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Expression Syntax (expression-syntax/)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Access current node data
|
||||||
|
{{$json.fieldName}}
|
||||||
|
{{$json.body.email}} // Webhook data is under .body!
|
||||||
|
|
||||||
|
// Access other nodes
|
||||||
|
{{$node["HTTP Request"].json.data}}
|
||||||
|
|
||||||
|
// Dates
|
||||||
|
{{$now.toFormat('yyyy-MM-dd')}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `SKILL.md` — Core syntax guide
|
||||||
|
- `EXAMPLES.md` — Common expression examples
|
||||||
|
- `COMMON_MISTAKES.md` — Pitfalls to avoid
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JavaScript Code (javascript-code/)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Basic Code node template
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
const processed = items.map(item => ({
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
processed: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return processed; // MUST return [{json: {...}}] format
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `SKILL.md` — Core guidance
|
||||||
|
- `DATA_ACCESS.md` — $input, $json patterns
|
||||||
|
- `BUILTIN_FUNCTIONS.md` — $helpers.httpRequest(), DateTime
|
||||||
|
- `COMMON_PATTERNS.md` — Reusable code snippets
|
||||||
|
- `ERROR_PATTERNS.md` — Debugging help
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Node Configuration (node-configuration/)
|
||||||
|
|
||||||
|
**Philosophy:** Progressive disclosure — start minimal, add complexity
|
||||||
|
|
||||||
|
1. **get_node_essentials** — Use first (91.7% success rate)
|
||||||
|
2. **get_property_dependencies** — For complex nodes
|
||||||
|
3. **get_node_info** — Full schema when needed
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `SKILL.md` — Core concepts
|
||||||
|
- `OPERATION_PATTERNS.md` — Node-specific patterns
|
||||||
|
- `DEPENDENCIES.md` — Property dependency chains
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Firefrost n8n Use Cases
|
||||||
|
|
||||||
|
| Workflow | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| Subscription sync | Stripe → Discord role assignment |
|
||||||
|
| Server monitoring | Pterodactyl → Discord alerts |
|
||||||
|
| Backup notifications | Server backup status → Discord |
|
||||||
|
| Welcome automation | New subscriber → Welcome email/DM |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
1. **Webhook data location:** Data is in `$json.body`, not `$json` directly
|
||||||
|
2. **Return format:** Code nodes MUST return `[{json: {...}}]`
|
||||||
|
3. **Node names:** Case-sensitive, must match exactly
|
||||||
|
4. **Expression braces:** Use `{{}}` not `{}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Source:** skill.fish
|
||||||
|
**Added:** 2026-04-09 by Chronicler #73
|
||||||
|
|
||||||
|
**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️
|
||||||
393
docs/skills/n8n-automation/expression-syntax/COMMON_MISTAKES.md
Normal file
393
docs/skills/n8n-automation/expression-syntax/COMMON_MISTAKES.md
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# Common n8n Expression Mistakes
|
||||||
|
|
||||||
|
Complete catalog of expression errors with explanations and fixes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Missing Curly Braces
|
||||||
|
|
||||||
|
**Problem**: Expression not recognized, shows as literal text
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
$json.email
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: n8n treats text without {{ }} as a literal string. Expressions must be wrapped to be evaluated.
|
||||||
|
|
||||||
|
**How to identify**: Field shows exact text like "$json.email" instead of actual value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Webhook Body Access
|
||||||
|
|
||||||
|
**Problem**: Undefined values when accessing webhook data
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$json.name}}
|
||||||
|
{{$json.email}}
|
||||||
|
{{$json.message}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.body.name}}
|
||||||
|
{{$json.body.email}}
|
||||||
|
{{$json.body.message}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Webhook node wraps incoming data under `.body` property. The root `$json` contains headers, params, query, and body.
|
||||||
|
|
||||||
|
**Webhook structure**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"headers": {...},
|
||||||
|
"params": {...},
|
||||||
|
"query": {...},
|
||||||
|
"body": { // User data is HERE!
|
||||||
|
"name": "John",
|
||||||
|
"email": "john@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to identify**: Webhook workflow shows "undefined" for fields that are definitely being sent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Spaces in Field Names
|
||||||
|
|
||||||
|
**Problem**: Syntax error or undefined value
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$json.first name}}
|
||||||
|
{{$json.user data.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json['first name']}}
|
||||||
|
{{$json['user data'].email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Spaces break dot notation. JavaScript interprets space as end of property name.
|
||||||
|
|
||||||
|
**How to identify**: Error message about unexpected token, or undefined when field exists.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Spaces in Node Names
|
||||||
|
|
||||||
|
**Problem**: Cannot access other node's data
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$node.HTTP Request.json.data}}
|
||||||
|
{{$node.Respond to Webhook.json}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$node["HTTP Request"].json.data}}
|
||||||
|
{{$node["Respond to Webhook"].json}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Node names are treated as object property names and need quotes when they contain spaces.
|
||||||
|
|
||||||
|
**How to identify**: Error like "Cannot read property 'Request' of undefined"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Incorrect Node Reference Case
|
||||||
|
|
||||||
|
**Problem**: Undefined or wrong data returned
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$node["http request"].json.data}} // lowercase
|
||||||
|
{{$node["Http Request"].json.data}} // wrong capitalization
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$node["HTTP Request"].json.data}} // exact match
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Node names are **case-sensitive**. Must match exactly as shown in workflow.
|
||||||
|
|
||||||
|
**How to identify**: Undefined value even though node exists and has data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Double Wrapping
|
||||||
|
|
||||||
|
**Problem**: Literal {{ }} appears in output
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{{$json.field}}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.field}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Only one set of {{ }} is needed. Extra braces are treated as literal characters.
|
||||||
|
|
||||||
|
**How to identify**: Output shows "{{value}}" instead of just "value".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Array Access with Dots
|
||||||
|
|
||||||
|
**Problem**: Syntax error or undefined
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$json.items.0.name}}
|
||||||
|
{{$json.users.1.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.items[0].name}}
|
||||||
|
{{$json.users[1].email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Array indices require brackets, not dots. Number after dot is invalid JavaScript.
|
||||||
|
|
||||||
|
**How to identify**: Syntax error or "Cannot read property '0' of undefined"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Using Expressions in Code Nodes
|
||||||
|
|
||||||
|
**Problem**: Literal string instead of value, or errors
|
||||||
|
|
||||||
|
❌ **Wrong (in Code node)**:
|
||||||
|
```javascript
|
||||||
|
const email = '{{$json.email}}';
|
||||||
|
const name = '={{$json.body.name}}';
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct (in Code node)**:
|
||||||
|
```javascript
|
||||||
|
const email = $json.email;
|
||||||
|
const name = $json.body.name;
|
||||||
|
|
||||||
|
// Or using Code node API
|
||||||
|
const email = $input.item.json.email;
|
||||||
|
const allItems = $input.all();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Code nodes have **direct access** to data. The {{ }} syntax is for expression fields in other nodes, not for JavaScript code.
|
||||||
|
|
||||||
|
**How to identify**: Literal string "{{$json.email}}" appears in Code node output instead of actual value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Missing Quotes in $node Reference
|
||||||
|
|
||||||
|
**Problem**: Syntax error
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$node[HTTP Request].json.data}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$node["HTTP Request"].json.data}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Node names must be quoted strings inside brackets.
|
||||||
|
|
||||||
|
**How to identify**: Syntax error "Unexpected identifier"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Incorrect Property Path
|
||||||
|
|
||||||
|
**Problem**: Undefined value
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$json.data.items.name}} // items is an array
|
||||||
|
{{$json.user.email}} // user doesn't exist, it's userData
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.data.items[0].name}} // access array element
|
||||||
|
{{$json.userData.email}} // correct property name
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Wrong path to data. Arrays need index, property names must be exact.
|
||||||
|
|
||||||
|
**How to identify**: Check actual data structure using expression editor preview.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Using = Prefix Outside JSON
|
||||||
|
|
||||||
|
**Problem**: Literal "=" appears in output
|
||||||
|
|
||||||
|
❌ **Wrong (in text field)**:
|
||||||
|
```
|
||||||
|
Email: ={{$json.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct (in text field)**:
|
||||||
|
```
|
||||||
|
Email: {{$json.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The `=` prefix is **only** needed in JSON mode or when you want to set entire field value to expression result:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// JSON mode (set property to expression)
|
||||||
|
{
|
||||||
|
"email": "={{$json.body.email}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text mode (no = needed)
|
||||||
|
Hello {{$json.body.name}}!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: The `=` is parsed as literal text in non-JSON contexts.
|
||||||
|
|
||||||
|
**How to identify**: Output shows "=john@example.com" instead of "john@example.com"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Expressions in Webhook Path
|
||||||
|
|
||||||
|
**Problem**: Path doesn't update, validation error
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
path: "{{$json.user_id}}/webhook"
|
||||||
|
path: "users/={{$env.TENANT_ID}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
path: "my-webhook" // Static paths only
|
||||||
|
path: "user-webhook/:userId" // Use dynamic URL parameters instead
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Webhook paths must be static. Use dynamic URL parameters (`:paramName`) instead of expressions.
|
||||||
|
|
||||||
|
**How to identify**: Webhook path doesn't change or validation warns about invalid path.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Forgetting .json in $node Reference
|
||||||
|
|
||||||
|
**Problem**: Undefined or wrong data
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{$node["HTTP Request"].data}} // Missing .json
|
||||||
|
{{$node["Webhook"].body.email}} // Missing .json
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$node["HTTP Request"].json.data}}
|
||||||
|
{{$node["Webhook"].json.body.email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Node data is always under `.json` property (or `.binary` for binary data).
|
||||||
|
|
||||||
|
**How to identify**: Undefined value when you know the node has data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. String Concatenation Confusion
|
||||||
|
|
||||||
|
**Problem**: Attempting JavaScript template literals
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
`Hello ${$json.name}!` // Template literal syntax
|
||||||
|
"Hello " + $json.name + "!" // String concatenation
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
Hello {{$json.name}}! // n8n expressions auto-concatenate
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: n8n expressions don't use JavaScript template literal syntax. Adjacent text and expressions are automatically concatenated.
|
||||||
|
|
||||||
|
**How to identify**: Literal backticks or + symbols appear in output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Empty Expression Brackets
|
||||||
|
|
||||||
|
**Problem**: Literal {{}} in output
|
||||||
|
|
||||||
|
❌ **Wrong**:
|
||||||
|
```
|
||||||
|
{{}}
|
||||||
|
{{ }}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **Correct**:
|
||||||
|
```
|
||||||
|
{{$json.field}} // Include expression content
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it fails**: Empty expression brackets have nothing to evaluate.
|
||||||
|
|
||||||
|
**How to identify**: Literal "{{ }}" text appears in output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference Table
|
||||||
|
|
||||||
|
| Error | Symptom | Fix |
|
||||||
|
|-------|---------|-----|
|
||||||
|
| No {{ }} | Literal text | Add {{ }} |
|
||||||
|
| Webhook data | Undefined | Add `.body` |
|
||||||
|
| Space in field | Syntax error | Use `['field name']` |
|
||||||
|
| Space in node | Undefined | Use `["Node Name"]` |
|
||||||
|
| Wrong case | Undefined | Match exact case |
|
||||||
|
| Double {{ }} | Literal braces | Remove extra {{ }} |
|
||||||
|
| .0 array | Syntax error | Use [0] |
|
||||||
|
| {{ }} in Code | Literal string | Remove {{ }} |
|
||||||
|
| No quotes in $node | Syntax error | Add quotes |
|
||||||
|
| Wrong path | Undefined | Check data structure |
|
||||||
|
| = in text | Literal = | Remove = prefix |
|
||||||
|
| Dynamic path | Doesn't work | Use static path |
|
||||||
|
| Missing .json | Undefined | Add .json |
|
||||||
|
| Template literals | Literal text | Use {{ }} |
|
||||||
|
| Empty {{ }} | Literal braces | Add expression |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debugging Process
|
||||||
|
|
||||||
|
When expression doesn't work:
|
||||||
|
|
||||||
|
1. **Check braces**: Is it wrapped in {{ }}?
|
||||||
|
2. **Check data source**: Is it webhook data? Add `.body`
|
||||||
|
3. **Check spaces**: Field or node name has spaces? Use brackets
|
||||||
|
4. **Check case**: Does node name match exactly?
|
||||||
|
5. **Check path**: Is the property path correct?
|
||||||
|
6. **Use expression editor**: Preview shows actual result
|
||||||
|
7. **Check context**: Is it a Code node? Remove {{ }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Related**: See [EXAMPLES.md](EXAMPLES.md) for working examples of correct syntax.
|
||||||
483
docs/skills/n8n-automation/expression-syntax/EXAMPLES.md
Normal file
483
docs/skills/n8n-automation/expression-syntax/EXAMPLES.md
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
# n8n Expression Examples
|
||||||
|
|
||||||
|
Real working examples from n8n workflows.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 1: Webhook Form Submission
|
||||||
|
|
||||||
|
**Scenario**: Form submission webhook posts to Slack
|
||||||
|
|
||||||
|
**Workflow**: Webhook → Slack
|
||||||
|
|
||||||
|
**Webhook Input** (POST):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com",
|
||||||
|
"company": "Acme Corp",
|
||||||
|
"message": "Interested in your product"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Webhook Node Output**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"headers": {"content-type": "application/json"},
|
||||||
|
"params": {},
|
||||||
|
"query": {},
|
||||||
|
"body": {
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com",
|
||||||
|
"company": "Acme Corp",
|
||||||
|
"message": "Interested in your product"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**In Slack Node** (text field):
|
||||||
|
```
|
||||||
|
New form submission! 📝
|
||||||
|
|
||||||
|
Name: {{$json.body.name}}
|
||||||
|
Email: {{$json.body.email}}
|
||||||
|
Company: {{$json.body.company}}
|
||||||
|
Message: {{$json.body.message}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```
|
||||||
|
New form submission! 📝
|
||||||
|
|
||||||
|
Name: John Doe
|
||||||
|
Email: john@example.com
|
||||||
|
Company: Acme Corp
|
||||||
|
Message: Interested in your product
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 2: HTTP API to Database
|
||||||
|
|
||||||
|
**Scenario**: Fetch user data from API and insert into database
|
||||||
|
|
||||||
|
**Workflow**: Schedule → HTTP Request → Postgres
|
||||||
|
|
||||||
|
**HTTP Request Returns**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": 123,
|
||||||
|
"name": "Alice Smith",
|
||||||
|
"email": "alice@example.com",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**In Postgres Node** (INSERT statement):
|
||||||
|
```sql
|
||||||
|
INSERT INTO users (user_id, name, email, role, synced_at)
|
||||||
|
VALUES (
|
||||||
|
{{$json.data.users[0].id}},
|
||||||
|
'{{$json.data.users[0].name}}',
|
||||||
|
'{{$json.data.users[0].email}}',
|
||||||
|
'{{$json.data.users[0].role}}',
|
||||||
|
'{{$now.toFormat('yyyy-MM-dd HH:mm:ss')}}'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result**: User inserted with current timestamp
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 3: Multi-Node Data Flow
|
||||||
|
|
||||||
|
**Scenario**: Webhook → HTTP Request → Email
|
||||||
|
|
||||||
|
**Workflow Structure**:
|
||||||
|
1. Webhook receives order ID
|
||||||
|
2. HTTP Request fetches order details
|
||||||
|
3. Email sends confirmation
|
||||||
|
|
||||||
|
### Node 1: Webhook
|
||||||
|
|
||||||
|
**Receives**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"order_id": "ORD-12345"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node 2: HTTP Request
|
||||||
|
|
||||||
|
**URL field**:
|
||||||
|
```
|
||||||
|
https://api.example.com/orders/{{$json.body.order_id}}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"order": {
|
||||||
|
"id": "ORD-12345",
|
||||||
|
"customer": "Bob Jones",
|
||||||
|
"total": 99.99,
|
||||||
|
"items": ["Widget", "Gadget"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Node 3: Email
|
||||||
|
|
||||||
|
**Subject**:
|
||||||
|
```
|
||||||
|
Order {{$node["Webhook"].json.body.order_id}} Confirmed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body**:
|
||||||
|
```
|
||||||
|
Dear {{$node["HTTP Request"].json.order.customer}},
|
||||||
|
|
||||||
|
Your order {{$node["Webhook"].json.body.order_id}} has been confirmed!
|
||||||
|
|
||||||
|
Total: ${{$node["HTTP Request"].json.order.total}}
|
||||||
|
Items: {{$node["HTTP Request"].json.order.items.join(', ')}}
|
||||||
|
|
||||||
|
Thank you for your purchase!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Email Result**:
|
||||||
|
```
|
||||||
|
Subject: Order ORD-12345 Confirmed
|
||||||
|
|
||||||
|
Dear Bob Jones,
|
||||||
|
|
||||||
|
Your order ORD-12345 has been confirmed!
|
||||||
|
|
||||||
|
Total: $99.99
|
||||||
|
Items: Widget, Gadget
|
||||||
|
|
||||||
|
Thank you for your purchase!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 4: Date Formatting
|
||||||
|
|
||||||
|
**Scenario**: Various date format outputs
|
||||||
|
|
||||||
|
**Current Time**: 2025-10-20 14:30:45
|
||||||
|
|
||||||
|
### ISO Format
|
||||||
|
```javascript
|
||||||
|
{{$now.toISO()}}
|
||||||
|
```
|
||||||
|
**Output**: `2025-10-20T14:30:45.000Z`
|
||||||
|
|
||||||
|
### Custom Date Format
|
||||||
|
```javascript
|
||||||
|
{{$now.toFormat('yyyy-MM-dd')}}
|
||||||
|
```
|
||||||
|
**Output**: `2025-10-20`
|
||||||
|
|
||||||
|
### Time Only
|
||||||
|
```javascript
|
||||||
|
{{$now.toFormat('HH:mm:ss')}}
|
||||||
|
```
|
||||||
|
**Output**: `14:30:45`
|
||||||
|
|
||||||
|
### Full Readable Format
|
||||||
|
```javascript
|
||||||
|
{{$now.toFormat('MMMM dd, yyyy')}}
|
||||||
|
```
|
||||||
|
**Output**: `October 20, 2025`
|
||||||
|
|
||||||
|
### Date Math - Future
|
||||||
|
```javascript
|
||||||
|
{{$now.plus({days: 7}).toFormat('yyyy-MM-dd')}}
|
||||||
|
```
|
||||||
|
**Output**: `2025-10-27`
|
||||||
|
|
||||||
|
### Date Math - Past
|
||||||
|
```javascript
|
||||||
|
{{$now.minus({hours: 24}).toFormat('yyyy-MM-dd HH:mm')}}
|
||||||
|
```
|
||||||
|
**Output**: `2025-10-19 14:30`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 5: Array Operations
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{"name": "Alice", "email": "alice@example.com"},
|
||||||
|
{"name": "Bob", "email": "bob@example.com"},
|
||||||
|
{"name": "Charlie", "email": "charlie@example.com"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### First User
|
||||||
|
```javascript
|
||||||
|
{{$json.users[0].name}}
|
||||||
|
```
|
||||||
|
**Output**: `Alice`
|
||||||
|
|
||||||
|
### Last User
|
||||||
|
```javascript
|
||||||
|
{{$json.users[$json.users.length - 1].name}}
|
||||||
|
```
|
||||||
|
**Output**: `Charlie`
|
||||||
|
|
||||||
|
### All Emails (Join)
|
||||||
|
```javascript
|
||||||
|
{{$json.users.map(u => u.email).join(', ')}}
|
||||||
|
```
|
||||||
|
**Output**: `alice@example.com, bob@example.com, charlie@example.com`
|
||||||
|
|
||||||
|
### Array Length
|
||||||
|
```javascript
|
||||||
|
{{$json.users.length}}
|
||||||
|
```
|
||||||
|
**Output**: `3`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 6: Conditional Logic
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"order": {
|
||||||
|
"status": "completed",
|
||||||
|
"total": 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ternary Operator
|
||||||
|
```javascript
|
||||||
|
{{$json.order.status === 'completed' ? 'Order Complete ✓' : 'Pending...'}}
|
||||||
|
```
|
||||||
|
**Output**: `Order Complete ✓`
|
||||||
|
|
||||||
|
### Default Values
|
||||||
|
```javascript
|
||||||
|
{{$json.order.notes || 'No notes provided'}}
|
||||||
|
```
|
||||||
|
**Output**: `No notes provided` (if notes field doesn't exist)
|
||||||
|
|
||||||
|
### Multiple Conditions
|
||||||
|
```javascript
|
||||||
|
{{$json.order.total > 100 ? 'Premium Customer' : 'Standard Customer'}}
|
||||||
|
```
|
||||||
|
**Output**: `Premium Customer`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 7: String Manipulation
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"email": "JOHN@EXAMPLE.COM",
|
||||||
|
"message": " Hello World "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lowercase
|
||||||
|
```javascript
|
||||||
|
{{$json.user.email.toLowerCase()}}
|
||||||
|
```
|
||||||
|
**Output**: `john@example.com`
|
||||||
|
|
||||||
|
### Uppercase
|
||||||
|
```javascript
|
||||||
|
{{$json.user.message.toUpperCase()}}
|
||||||
|
```
|
||||||
|
**Output**: ` HELLO WORLD `
|
||||||
|
|
||||||
|
### Trim
|
||||||
|
```javascript
|
||||||
|
{{$json.user.message.trim()}}
|
||||||
|
```
|
||||||
|
**Output**: `Hello World`
|
||||||
|
|
||||||
|
### Substring
|
||||||
|
```javascript
|
||||||
|
{{$json.user.email.substring(0, 4)}}
|
||||||
|
```
|
||||||
|
**Output**: `JOHN`
|
||||||
|
|
||||||
|
### Replace
|
||||||
|
```javascript
|
||||||
|
{{$json.user.message.replace('World', 'n8n')}}
|
||||||
|
```
|
||||||
|
**Output**: ` Hello n8n `
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 8: Fields with Spaces
|
||||||
|
|
||||||
|
**Data**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user data": {
|
||||||
|
"first name": "Jane",
|
||||||
|
"last name": "Doe",
|
||||||
|
"phone number": "+1234567890"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bracket Notation
|
||||||
|
```javascript
|
||||||
|
{{$json['user data']['first name']}}
|
||||||
|
```
|
||||||
|
**Output**: `Jane`
|
||||||
|
|
||||||
|
### Combined
|
||||||
|
```javascript
|
||||||
|
{{$json['user data']['first name']}} {{$json['user data']['last name']}}
|
||||||
|
```
|
||||||
|
**Output**: `Jane Doe`
|
||||||
|
|
||||||
|
### Nested Spaces
|
||||||
|
```javascript
|
||||||
|
Contact: {{$json['user data']['phone number']}}
|
||||||
|
```
|
||||||
|
**Output**: `Contact: +1234567890`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 9: Code Node (Direct Access)
|
||||||
|
|
||||||
|
**Code Node**: Transform webhook data
|
||||||
|
|
||||||
|
**Input** (from Webhook node):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"items": ["apple", "banana", "cherry"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Code** (JavaScript):
|
||||||
|
```javascript
|
||||||
|
// ✅ Direct access (no {{ }})
|
||||||
|
const items = $json.body.items;
|
||||||
|
|
||||||
|
// Transform to uppercase
|
||||||
|
const uppercased = items.map(item => item.toUpperCase());
|
||||||
|
|
||||||
|
// Return in n8n format
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
original: items,
|
||||||
|
transformed: uppercased,
|
||||||
|
count: items.length
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"original": ["apple", "banana", "cherry"],
|
||||||
|
"transformed": ["APPLE", "BANANA", "CHERRY"],
|
||||||
|
"count": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 10: Environment Variables
|
||||||
|
|
||||||
|
**Setup**: Environment variable `API_KEY=secret123`
|
||||||
|
|
||||||
|
### In HTTP Request (Headers)
|
||||||
|
```javascript
|
||||||
|
Authorization: Bearer {{$env.API_KEY}}
|
||||||
|
```
|
||||||
|
**Result**: `Authorization: Bearer secret123`
|
||||||
|
|
||||||
|
### In URL
|
||||||
|
```javascript
|
||||||
|
https://api.example.com/data?key={{$env.API_KEY}}
|
||||||
|
```
|
||||||
|
**Result**: `https://api.example.com/data?key=secret123`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Template from Real Workflow
|
||||||
|
|
||||||
|
**Based on n8n template #2947** (Weather to Slack)
|
||||||
|
|
||||||
|
### Workflow Structure
|
||||||
|
Webhook → OpenStreetMap API → Weather API → Slack
|
||||||
|
|
||||||
|
### Webhook Slash Command
|
||||||
|
**Input**: `/weather London`
|
||||||
|
|
||||||
|
**Webhook receives**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"text": "London"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### OpenStreetMap API
|
||||||
|
**URL**:
|
||||||
|
```
|
||||||
|
https://nominatim.openstreetmap.org/search?q={{$json.body.text}}&format=json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weather API (NWS)
|
||||||
|
**URL**:
|
||||||
|
```
|
||||||
|
https://api.weather.gov/points/{{$node["OpenStreetMap"].json[0].lat}},{{$node["OpenStreetMap"].json[0].lon}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slack Message
|
||||||
|
```
|
||||||
|
Weather for {{$json.body.text}}:
|
||||||
|
|
||||||
|
Temperature: {{$node["Weather API"].json.properties.temperature.value}}°C
|
||||||
|
Conditions: {{$node["Weather API"].json.properties.shortForecast}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
1. Webhook data is under `.body`
|
||||||
|
2. Use `{{}}` for expressions (except Code nodes)
|
||||||
|
3. Reference other nodes with `$node["Node Name"].json`
|
||||||
|
4. Use brackets for field names with spaces
|
||||||
|
5. Node names are case-sensitive
|
||||||
|
|
||||||
|
**Most Common Uses**:
|
||||||
|
- `{{$json.body.field}}` - Webhook data
|
||||||
|
- `{{$node["Name"].json.field}}` - Other node data
|
||||||
|
- `{{$now.toFormat('yyyy-MM-dd')}}` - Timestamps
|
||||||
|
- `{{$json.array[0].field}}` - Array access
|
||||||
|
- `{{$json.field || 'default'}}` - Default values
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Related**: See [COMMON_MISTAKES.md](COMMON_MISTAKES.md) for error examples and fixes.
|
||||||
93
docs/skills/n8n-automation/expression-syntax/README.md
Normal file
93
docs/skills/n8n-automation/expression-syntax/README.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# n8n Expression Syntax
|
||||||
|
|
||||||
|
Expert guide for writing correct n8n expressions in workflows.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Teaches correct n8n expression syntax ({{ }} patterns) and fixes common mistakes, especially the critical webhook data structure gotcha.
|
||||||
|
|
||||||
|
## Activates On
|
||||||
|
|
||||||
|
- expression
|
||||||
|
- {{}} syntax
|
||||||
|
- $json, $node, $now, $env
|
||||||
|
- webhook data
|
||||||
|
- troubleshoot expression error
|
||||||
|
- undefined in workflow
|
||||||
|
|
||||||
|
## File Count
|
||||||
|
|
||||||
|
4 files, ~450 lines total
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
**n8n-mcp tools**:
|
||||||
|
- None directly (syntax knowledge skill)
|
||||||
|
- Works with n8n-mcp validation tools
|
||||||
|
|
||||||
|
**Related skills**:
|
||||||
|
- n8n Workflow Patterns (uses expressions in examples)
|
||||||
|
- n8n MCP Tools Expert (validates expressions)
|
||||||
|
- n8n Node Configuration (when expressions are needed)
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
### Core Topics
|
||||||
|
- Expression format ({{ }})
|
||||||
|
- Core variables ($json, $node, $now, $env)
|
||||||
|
- **Webhook data structure** ($json.body.*)
|
||||||
|
- When NOT to use expressions (Code nodes)
|
||||||
|
|
||||||
|
### Common Patterns
|
||||||
|
- Accessing nested fields
|
||||||
|
- Referencing other nodes
|
||||||
|
- Array and object access
|
||||||
|
- Date/time formatting
|
||||||
|
- String manipulation
|
||||||
|
|
||||||
|
### Error Prevention
|
||||||
|
- 15 common mistakes with fixes
|
||||||
|
- Quick reference table
|
||||||
|
- Debugging process
|
||||||
|
|
||||||
|
## Evaluations
|
||||||
|
|
||||||
|
4 scenarios (100% coverage expected):
|
||||||
|
1. **eval-001**: Missing curly braces
|
||||||
|
2. **eval-002**: Webhook body data access (critical!)
|
||||||
|
3. **eval-003**: Code node vs expression confusion
|
||||||
|
4. **eval-004**: Node reference syntax
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
✅ **Critical Gotcha Highlighted**: Webhook data under `.body`
|
||||||
|
✅ **Real Examples**: From MCP testing and real templates
|
||||||
|
✅ **Quick Fixes Table**: Fast reference for common errors
|
||||||
|
✅ **Code vs Expression**: Clear distinction
|
||||||
|
✅ **Comprehensive**: Covers 95% of expression use cases
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- **SKILL.md** (285 lines) - Main content with all essential knowledge
|
||||||
|
- **COMMON_MISTAKES.md** (380 lines) - Complete error catalog with 15 common mistakes
|
||||||
|
- **EXAMPLES.md** (450 lines) - 10 real working examples
|
||||||
|
- **README.md** (this file) - Skill metadata
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
**Expected outcomes**:
|
||||||
|
- Users correctly wrap expressions in {{ }}
|
||||||
|
- Zero webhook `.body` access errors
|
||||||
|
- No expressions used in Code nodes
|
||||||
|
- Correct $node reference syntax
|
||||||
|
|
||||||
|
## Last Updated
|
||||||
|
|
||||||
|
2025-10-20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Part of**: n8n-skills repository
|
||||||
|
**Conceived by**: Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
|
||||||
516
docs/skills/n8n-automation/expression-syntax/SKILL.md
Normal file
516
docs/skills/n8n-automation/expression-syntax/SKILL.md
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
---
|
||||||
|
name: n8n-expression-syntax
|
||||||
|
description: Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{{$json.fieldName}}
|
||||||
|
{{$json['field with spaces']}}
|
||||||
|
{{$json.nested.property}}
|
||||||
|
{{$json.items[0].name}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### $node - Reference Other Nodes
|
||||||
|
|
||||||
|
Access data from any previous node:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{{$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:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{{$now}}
|
||||||
|
{{$now.toFormat('yyyy-MM-dd')}}
|
||||||
|
{{$now.toFormat('HH:mm:ss')}}
|
||||||
|
{{$now.plus({days: 7})}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### $env - Environment Variables
|
||||||
|
|
||||||
|
Access environment variables:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{{$env.API_KEY}}
|
||||||
|
{{$env.DATABASE_URL}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 CRITICAL: Webhook Data Structure
|
||||||
|
|
||||||
|
**Most Common Mistake**: Webhook data is **NOT** at the root!
|
||||||
|
|
||||||
|
### Webhook Node Output Structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"headers": {...},
|
||||||
|
"params": {...},
|
||||||
|
"query": {...},
|
||||||
|
"body": { // ⚠️ USER DATA IS HERE!
|
||||||
|
"name": "John",
|
||||||
|
"email": "john@example.com",
|
||||||
|
"message": "Hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Correct Webhook Data Access
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
❌ 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 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!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG
|
||||||
|
path: "{{$json.user_id}}/webhook"
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
path: "user-webhook" // Static paths only
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ Credential Fields
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG
|
||||||
|
apiKey: "={{$env.API_KEY}}"
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
Use n8n credential system, not expressions
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Rules
|
||||||
|
|
||||||
|
### 1. Always Use {{}}
|
||||||
|
|
||||||
|
Expressions **must** be wrapped in double curly braces.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
❌ $json.field
|
||||||
|
✅ {{$json.field}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Use Quotes for Spaces
|
||||||
|
|
||||||
|
Field or node names with spaces require **bracket notation**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
❌ {{$json.field name}}
|
||||||
|
✅ {{$json['field name']}}
|
||||||
|
|
||||||
|
❌ {{$node.HTTP Request.json}}
|
||||||
|
✅ {{$node["HTTP Request"].json}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Match Exact Node Names
|
||||||
|
|
||||||
|
Node references are **case-sensitive**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
❌ {{$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:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
❌ {{{$json.field}}}
|
||||||
|
✅ {{$json.field}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Mistakes
|
||||||
|
|
||||||
|
For complete error catalog with fixes, see [COMMON_MISTAKES.md](COMMON_MISTAKES.md)
|
||||||
|
|
||||||
|
### Quick Fixes
|
||||||
|
|
||||||
|
| Mistake | Fix |
|
||||||
|
|---------|-----|
|
||||||
|
| `$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](EXAMPLES.md)
|
||||||
|
|
||||||
|
### Example 1: Webhook to Slack
|
||||||
|
|
||||||
|
**Webhook receives**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// First item
|
||||||
|
{{$json.users[0].email}}
|
||||||
|
|
||||||
|
// Array length
|
||||||
|
{{$json.users.length}}
|
||||||
|
|
||||||
|
// Last item
|
||||||
|
{{$json.users[$json.users.length - 1].name}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Objects
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Dot notation (no spaces)
|
||||||
|
{{$json.user.email}}
|
||||||
|
|
||||||
|
// Bracket notation (with spaces or dynamic)
|
||||||
|
{{$json['user data'].email}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strings
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Concatenation (automatic)
|
||||||
|
Hello {{$json.name}}!
|
||||||
|
|
||||||
|
// String methods
|
||||||
|
{{$json.email.toLowerCase()}}
|
||||||
|
{{$json.name.toUpperCase()}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Numbers
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Direct use
|
||||||
|
{{$json.price}}
|
||||||
|
|
||||||
|
// Math operations
|
||||||
|
{{$json.price * 1.1}} // Add 10%
|
||||||
|
{{$json.quantity + 5}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Conditional Content
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Ternary operator
|
||||||
|
{{$json.status === 'active' ? 'Active User' : 'Inactive User'}}
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
{{$json.email || 'no-email@example.com'}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Date Manipulation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 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
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Substring
|
||||||
|
{{$json.email.substring(0, 5)}}
|
||||||
|
|
||||||
|
// Replace
|
||||||
|
{{$json.message.replace('old', 'new')}}
|
||||||
|
|
||||||
|
// Split and join
|
||||||
|
{{$json.tags.split(',').join(', ')}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
- [COMMON_MISTAKES.md](COMMON_MISTAKES.md) - Complete error catalog
|
||||||
|
- [EXAMPLES.md](EXAMPLES.md) - Real workflow examples
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Need Help?** Reference the n8n expression documentation or use n8n-mcp validation tools to check your expressions.
|
||||||
764
docs/skills/n8n-automation/javascript-code/BUILTIN_FUNCTIONS.md
Normal file
764
docs/skills/n8n-automation/javascript-code/BUILTIN_FUNCTIONS.md
Normal file
@@ -0,0 +1,764 @@
|
|||||||
|
# Built-in Functions - JavaScript Code Node
|
||||||
|
|
||||||
|
Complete reference for n8n's built-in JavaScript functions and helpers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
n8n Code nodes provide powerful built-in functions beyond standard JavaScript. This guide covers:
|
||||||
|
|
||||||
|
1. **$helpers.httpRequest()** - Make HTTP requests
|
||||||
|
2. **DateTime (Luxon)** - Advanced date/time operations
|
||||||
|
3. **$jmespath()** - Query JSON structures
|
||||||
|
4. **$getWorkflowStaticData()** - Persistent storage
|
||||||
|
5. **Standard JavaScript Globals** - Math, JSON, console, etc.
|
||||||
|
6. **Available Node.js Modules** - crypto, Buffer, URL
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. $helpers.httpRequest() - HTTP Requests
|
||||||
|
|
||||||
|
Make HTTP requests directly from Code nodes without using HTTP Request node.
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://api.example.com/users'
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: {data: response}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complete Options
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
method: 'POST', // GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
|
||||||
|
url: 'https://api.example.com/users',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer token123',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'n8n-workflow'
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name: 'John Doe',
|
||||||
|
email: 'john@example.com'
|
||||||
|
},
|
||||||
|
qs: { // Query string parameters
|
||||||
|
page: 1,
|
||||||
|
limit: 10
|
||||||
|
},
|
||||||
|
timeout: 10000, // Milliseconds (default: no timeout)
|
||||||
|
json: true, // Auto-parse JSON response (default: true)
|
||||||
|
simple: false, // Don't throw on HTTP errors (default: true)
|
||||||
|
resolveWithFullResponse: false // Return only body (default: false)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### GET Request
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Simple GET
|
||||||
|
const users = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://api.example.com/users'
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: {users}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// GET with query parameters
|
||||||
|
const results = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://api.example.com/search',
|
||||||
|
qs: {
|
||||||
|
q: 'javascript',
|
||||||
|
page: 1,
|
||||||
|
per_page: 50
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: results}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST Request
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// POST with JSON body
|
||||||
|
const newUser = await $helpers.httpRequest({
|
||||||
|
method: 'POST',
|
||||||
|
url: 'https://api.example.com/users',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer ' + $env.API_KEY
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name: $json.body.name,
|
||||||
|
email: $json.body.email,
|
||||||
|
role: 'user'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: newUser}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### PUT/PATCH Request
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update resource
|
||||||
|
const updated = await $helpers.httpRequest({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: `https://api.example.com/users/${userId}`,
|
||||||
|
body: {
|
||||||
|
name: 'Updated Name',
|
||||||
|
status: 'active'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: updated}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### DELETE Request
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Delete resource
|
||||||
|
await $helpers.httpRequest({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: `https://api.example.com/users/${userId}`,
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + $env.API_KEY
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: {deleted: true, userId}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication Patterns
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Bearer Token
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
url: 'https://api.example.com/data',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${$env.API_TOKEN}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// API Key in Header
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
url: 'https://api.example.com/data',
|
||||||
|
headers: {
|
||||||
|
'X-API-Key': $env.API_KEY
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Basic Auth (manual)
|
||||||
|
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
||||||
|
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
url: 'https://api.example.com/data',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Basic ${credentials}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Handle HTTP errors gracefully
|
||||||
|
try {
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://api.example.com/users',
|
||||||
|
simple: false // Don't throw on 4xx/5xx
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
|
return [{json: {success: true, data: response.body}}];
|
||||||
|
} else {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
success: false,
|
||||||
|
status: response.statusCode,
|
||||||
|
error: response.body
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full Response Access
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get full response including headers and status
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
url: 'https://api.example.com/data',
|
||||||
|
resolveWithFullResponse: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
statusCode: response.statusCode,
|
||||||
|
headers: response.headers,
|
||||||
|
body: response.body,
|
||||||
|
rateLimit: response.headers['x-ratelimit-remaining']
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. DateTime (Luxon) - Date & Time Operations
|
||||||
|
|
||||||
|
n8n includes Luxon for powerful date/time handling. Access via `DateTime` global.
|
||||||
|
|
||||||
|
### Current Date/Time
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Current time
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
// Current time in specific timezone
|
||||||
|
const nowTokyo = DateTime.now().setZone('Asia/Tokyo');
|
||||||
|
|
||||||
|
// Today at midnight
|
||||||
|
const today = DateTime.now().startOf('day');
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
iso: now.toISO(), // "2025-01-20T15:30:00.000Z"
|
||||||
|
formatted: now.toFormat('yyyy-MM-dd HH:mm:ss'), // "2025-01-20 15:30:00"
|
||||||
|
unix: now.toSeconds(), // Unix timestamp
|
||||||
|
millis: now.toMillis() // Milliseconds since epoch
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Formatting Dates
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
isoFormat: now.toISO(), // ISO 8601: "2025-01-20T15:30:00.000Z"
|
||||||
|
sqlFormat: now.toSQL(), // SQL: "2025-01-20 15:30:00.000"
|
||||||
|
httpFormat: now.toHTTP(), // HTTP: "Mon, 20 Jan 2025 15:30:00 GMT"
|
||||||
|
|
||||||
|
// Custom formats
|
||||||
|
dateOnly: now.toFormat('yyyy-MM-dd'), // "2025-01-20"
|
||||||
|
timeOnly: now.toFormat('HH:mm:ss'), // "15:30:00"
|
||||||
|
readable: now.toFormat('MMMM dd, yyyy'), // "January 20, 2025"
|
||||||
|
compact: now.toFormat('yyyyMMdd'), // "20250120"
|
||||||
|
withDay: now.toFormat('EEEE, MMMM dd, yyyy'), // "Monday, January 20, 2025"
|
||||||
|
custom: now.toFormat('dd/MM/yy HH:mm') // "20/01/25 15:30"
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parsing Dates
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// From ISO string
|
||||||
|
const dt1 = DateTime.fromISO('2025-01-20T15:30:00');
|
||||||
|
|
||||||
|
// From specific format
|
||||||
|
const dt2 = DateTime.fromFormat('01/20/2025', 'MM/dd/yyyy');
|
||||||
|
|
||||||
|
// From SQL
|
||||||
|
const dt3 = DateTime.fromSQL('2025-01-20 15:30:00');
|
||||||
|
|
||||||
|
// From Unix timestamp
|
||||||
|
const dt4 = DateTime.fromSeconds(1737384600);
|
||||||
|
|
||||||
|
// From milliseconds
|
||||||
|
const dt5 = DateTime.fromMillis(1737384600000);
|
||||||
|
|
||||||
|
return [{json: {parsed: dt1.toISO()}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Date Arithmetic
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Adding time
|
||||||
|
tomorrow: now.plus({days: 1}).toISO(),
|
||||||
|
nextWeek: now.plus({weeks: 1}).toISO(),
|
||||||
|
nextMonth: now.plus({months: 1}).toISO(),
|
||||||
|
inTwoHours: now.plus({hours: 2}).toISO(),
|
||||||
|
|
||||||
|
// Subtracting time
|
||||||
|
yesterday: now.minus({days: 1}).toISO(),
|
||||||
|
lastWeek: now.minus({weeks: 1}).toISO(),
|
||||||
|
lastMonth: now.minus({months: 1}).toISO(),
|
||||||
|
twoHoursAgo: now.minus({hours: 2}).toISO(),
|
||||||
|
|
||||||
|
// Complex operations
|
||||||
|
in90Days: now.plus({days: 90}).toFormat('yyyy-MM-dd'),
|
||||||
|
in6Months: now.plus({months: 6}).toFormat('yyyy-MM-dd')
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time Comparisons
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
const targetDate = DateTime.fromISO('2025-12-31');
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Comparisons
|
||||||
|
isFuture: targetDate > now,
|
||||||
|
isPast: targetDate < now,
|
||||||
|
isEqual: targetDate.equals(now),
|
||||||
|
|
||||||
|
// Differences
|
||||||
|
daysUntil: targetDate.diff(now, 'days').days,
|
||||||
|
hoursUntil: targetDate.diff(now, 'hours').hours,
|
||||||
|
monthsUntil: targetDate.diff(now, 'months').months,
|
||||||
|
|
||||||
|
// Detailed difference
|
||||||
|
detailedDiff: targetDate.diff(now, ['months', 'days', 'hours']).toObject()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timezone Operations
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Current timezone
|
||||||
|
local: now.toISO(),
|
||||||
|
|
||||||
|
// Convert to different timezone
|
||||||
|
tokyo: now.setZone('Asia/Tokyo').toISO(),
|
||||||
|
newYork: now.setZone('America/New_York').toISO(),
|
||||||
|
london: now.setZone('Europe/London').toISO(),
|
||||||
|
utc: now.toUTC().toISO(),
|
||||||
|
|
||||||
|
// Get timezone info
|
||||||
|
timezone: now.zoneName, // "America/Los_Angeles"
|
||||||
|
offset: now.offset, // Offset in minutes
|
||||||
|
offsetFormatted: now.toFormat('ZZ') // "+08:00"
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start/End of Period
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
startOfDay: now.startOf('day').toISO(),
|
||||||
|
endOfDay: now.endOf('day').toISO(),
|
||||||
|
startOfWeek: now.startOf('week').toISO(),
|
||||||
|
endOfWeek: now.endOf('week').toISO(),
|
||||||
|
startOfMonth: now.startOf('month').toISO(),
|
||||||
|
endOfMonth: now.endOf('month').toISO(),
|
||||||
|
startOfYear: now.startOf('year').toISO(),
|
||||||
|
endOfYear: now.endOf('year').toISO()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Weekday & Month Info
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Day info
|
||||||
|
weekday: now.weekday, // 1 = Monday, 7 = Sunday
|
||||||
|
weekdayShort: now.weekdayShort, // "Mon"
|
||||||
|
weekdayLong: now.weekdayLong, // "Monday"
|
||||||
|
isWeekend: now.weekday > 5, // Saturday or Sunday
|
||||||
|
|
||||||
|
// Month info
|
||||||
|
month: now.month, // 1-12
|
||||||
|
monthShort: now.monthShort, // "Jan"
|
||||||
|
monthLong: now.monthLong, // "January"
|
||||||
|
|
||||||
|
// Year info
|
||||||
|
year: now.year, // 2025
|
||||||
|
quarter: now.quarter, // 1-4
|
||||||
|
daysInMonth: now.daysInMonth // 28-31
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. $jmespath() - JSON Querying
|
||||||
|
|
||||||
|
Query and transform JSON structures using JMESPath syntax.
|
||||||
|
|
||||||
|
### Basic Queries
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const data = $input.first().json;
|
||||||
|
|
||||||
|
// Extract specific field
|
||||||
|
const names = $jmespath(data, 'users[*].name');
|
||||||
|
|
||||||
|
// Filter array
|
||||||
|
const adults = $jmespath(data, 'users[?age >= `18`]');
|
||||||
|
|
||||||
|
// Get specific index
|
||||||
|
const firstUser = $jmespath(data, 'users[0]');
|
||||||
|
|
||||||
|
return [{json: {names, adults, firstUser}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Queries
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const data = $input.first().json;
|
||||||
|
|
||||||
|
// Sort and slice
|
||||||
|
const top5 = $jmespath(data, 'users | sort_by(@, &score) | reverse(@) | [0:5]');
|
||||||
|
|
||||||
|
// Extract nested fields
|
||||||
|
const emails = $jmespath(data, 'users[*].contact.email');
|
||||||
|
|
||||||
|
// Multi-field extraction
|
||||||
|
const simplified = $jmespath(data, 'users[*].{name: name, email: contact.email}');
|
||||||
|
|
||||||
|
// Conditional filtering
|
||||||
|
const premium = $jmespath(data, 'users[?subscription.tier == `premium`]');
|
||||||
|
|
||||||
|
return [{json: {top5, emails, simplified, premium}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Patterns
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Pattern 1: Filter and project
|
||||||
|
const query1 = $jmespath(data, 'products[?price > `100`].{name: name, price: price}');
|
||||||
|
|
||||||
|
// Pattern 2: Aggregate functions
|
||||||
|
const query2 = $jmespath(data, 'sum(products[*].price)');
|
||||||
|
const query3 = $jmespath(data, 'max(products[*].price)');
|
||||||
|
const query4 = $jmespath(data, 'length(products)');
|
||||||
|
|
||||||
|
// Pattern 3: Nested filtering
|
||||||
|
const query5 = $jmespath(data, 'categories[*].products[?inStock == `true`]');
|
||||||
|
|
||||||
|
return [{json: {query1, query2, query3, query4, query5}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. $getWorkflowStaticData() - Persistent Storage
|
||||||
|
|
||||||
|
Store data that persists across workflow executions.
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get static data storage
|
||||||
|
const staticData = $getWorkflowStaticData();
|
||||||
|
|
||||||
|
// Initialize counter if doesn't exist
|
||||||
|
if (!staticData.counter) {
|
||||||
|
staticData.counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment counter
|
||||||
|
staticData.counter++;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
executionCount: staticData.counter
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use Case 1: Rate limiting
|
||||||
|
const staticData = $getWorkflowStaticData();
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (!staticData.lastRun) {
|
||||||
|
staticData.lastRun = now;
|
||||||
|
staticData.runCount = 1;
|
||||||
|
} else {
|
||||||
|
const timeSinceLastRun = now - staticData.lastRun;
|
||||||
|
|
||||||
|
if (timeSinceLastRun < 60000) { // Less than 1 minute
|
||||||
|
return [{json: {error: 'Rate limit: wait 1 minute between runs'}}];
|
||||||
|
}
|
||||||
|
|
||||||
|
staticData.lastRun = now;
|
||||||
|
staticData.runCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{json: {allowed: true, totalRuns: staticData.runCount}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use Case 2: Tracking last processed ID
|
||||||
|
const staticData = $getWorkflowStaticData();
|
||||||
|
const currentItems = $input.all();
|
||||||
|
|
||||||
|
// Get last processed ID
|
||||||
|
const lastId = staticData.lastProcessedId || 0;
|
||||||
|
|
||||||
|
// Filter only new items
|
||||||
|
const newItems = currentItems.filter(item => item.json.id > lastId);
|
||||||
|
|
||||||
|
// Update last processed ID
|
||||||
|
if (newItems.length > 0) {
|
||||||
|
staticData.lastProcessedId = Math.max(...newItems.map(item => item.json.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newItems;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use Case 3: Accumulating results
|
||||||
|
const staticData = $getWorkflowStaticData();
|
||||||
|
|
||||||
|
if (!staticData.accumulated) {
|
||||||
|
staticData.accumulated = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current items to accumulated list
|
||||||
|
const currentData = $input.all().map(item => item.json);
|
||||||
|
staticData.accumulated.push(...currentData);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
currentBatch: currentData.length,
|
||||||
|
totalAccumulated: staticData.accumulated.length,
|
||||||
|
allData: staticData.accumulated
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Standard JavaScript Globals
|
||||||
|
|
||||||
|
### Math Object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Rounding
|
||||||
|
rounded: Math.round(3.7), // 4
|
||||||
|
floor: Math.floor(3.7), // 3
|
||||||
|
ceil: Math.ceil(3.2), // 4
|
||||||
|
|
||||||
|
// Min/Max
|
||||||
|
max: Math.max(1, 5, 3, 9, 2), // 9
|
||||||
|
min: Math.min(1, 5, 3, 9, 2), // 1
|
||||||
|
|
||||||
|
// Random
|
||||||
|
random: Math.random(), // 0-1
|
||||||
|
randomInt: Math.floor(Math.random() * 100), // 0-99
|
||||||
|
|
||||||
|
// Other
|
||||||
|
abs: Math.abs(-5), // 5
|
||||||
|
sqrt: Math.sqrt(16), // 4
|
||||||
|
pow: Math.pow(2, 3) // 8
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON Object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Parse JSON string
|
||||||
|
const jsonString = '{"name": "John", "age": 30}';
|
||||||
|
const parsed = JSON.parse(jsonString);
|
||||||
|
|
||||||
|
// Stringify object
|
||||||
|
const obj = {name: "John", age: 30};
|
||||||
|
const stringified = JSON.stringify(obj);
|
||||||
|
|
||||||
|
// Pretty print
|
||||||
|
const pretty = JSON.stringify(obj, null, 2);
|
||||||
|
|
||||||
|
return [{json: {parsed, stringified, pretty}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### console Object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Debug logging (appears in browser console, press F12)
|
||||||
|
console.log('Processing items:', $input.all().length);
|
||||||
|
console.log('First item:', $input.first().json);
|
||||||
|
|
||||||
|
// Other console methods
|
||||||
|
console.error('Error message');
|
||||||
|
console.warn('Warning message');
|
||||||
|
console.info('Info message');
|
||||||
|
|
||||||
|
// Continues to return data
|
||||||
|
return [{json: {processed: true}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Methods
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const obj = {name: "John", age: 30, city: "NYC"};
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
keys: Object.keys(obj), // ["name", "age", "city"]
|
||||||
|
values: Object.values(obj), // ["John", 30, "NYC"]
|
||||||
|
entries: Object.entries(obj), // [["name", "John"], ...]
|
||||||
|
|
||||||
|
// Check property
|
||||||
|
hasName: 'name' in obj, // true
|
||||||
|
|
||||||
|
// Merge objects
|
||||||
|
merged: Object.assign({}, obj, {country: "USA"})
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array Methods
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const arr = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
mapped: arr.map(x => x * 2), // [2, 4, 6, 8, 10]
|
||||||
|
filtered: arr.filter(x => x > 2), // [3, 4, 5]
|
||||||
|
reduced: arr.reduce((sum, x) => sum + x, 0), // 15
|
||||||
|
some: arr.some(x => x > 3), // true
|
||||||
|
every: arr.every(x => x > 0), // true
|
||||||
|
find: arr.find(x => x > 3), // 4
|
||||||
|
includes: arr.includes(3), // true
|
||||||
|
joined: arr.join(', ') // "1, 2, 3, 4, 5"
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Available Node.js Modules
|
||||||
|
|
||||||
|
### crypto Module
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Hash functions
|
||||||
|
const hash = crypto.createHash('sha256')
|
||||||
|
.update('my secret text')
|
||||||
|
.digest('hex');
|
||||||
|
|
||||||
|
// MD5 hash
|
||||||
|
const md5 = crypto.createHash('md5')
|
||||||
|
.update('my text')
|
||||||
|
.digest('hex');
|
||||||
|
|
||||||
|
// Random values
|
||||||
|
const randomBytes = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
return [{json: {hash, md5, randomBytes}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buffer (built-in)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Base64 encoding
|
||||||
|
const encoded = Buffer.from('Hello World').toString('base64');
|
||||||
|
|
||||||
|
// Base64 decoding
|
||||||
|
const decoded = Buffer.from(encoded, 'base64').toString();
|
||||||
|
|
||||||
|
// Hex encoding
|
||||||
|
const hex = Buffer.from('Hello').toString('hex');
|
||||||
|
|
||||||
|
return [{json: {encoded, decoded, hex}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### URL / URLSearchParams
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Parse URL
|
||||||
|
const url = new URL('https://example.com/path?param1=value1¶m2=value2');
|
||||||
|
|
||||||
|
// Build query string
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
search: 'query',
|
||||||
|
page: 1,
|
||||||
|
limit: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
host: url.host,
|
||||||
|
pathname: url.pathname,
|
||||||
|
search: url.search,
|
||||||
|
queryString: params.toString() // "search=query&page=1&limit=10"
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What's NOT Available
|
||||||
|
|
||||||
|
**External npm packages are NOT available:**
|
||||||
|
- ❌ axios
|
||||||
|
- ❌ lodash
|
||||||
|
- ❌ moment (use DateTime/Luxon instead)
|
||||||
|
- ❌ request
|
||||||
|
- ❌ Any other npm package
|
||||||
|
|
||||||
|
**Workaround**: Use $helpers.httpRequest() for HTTP, or add data to workflow via HTTP Request node.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Most Useful Built-ins**:
|
||||||
|
1. **$helpers.httpRequest()** - API calls without HTTP Request node
|
||||||
|
2. **DateTime** - Professional date/time handling
|
||||||
|
3. **$jmespath()** - Complex JSON queries
|
||||||
|
4. **Math, JSON, Object, Array** - Standard JavaScript utilities
|
||||||
|
|
||||||
|
**Common Patterns**:
|
||||||
|
- API calls: Use $helpers.httpRequest()
|
||||||
|
- Date operations: Use DateTime (Luxon)
|
||||||
|
- Data filtering: Use $jmespath() or JavaScript .filter()
|
||||||
|
- Persistent data: Use $getWorkflowStaticData()
|
||||||
|
- Hashing: Use crypto module
|
||||||
|
|
||||||
|
**See Also**:
|
||||||
|
- [SKILL.md](SKILL.md) - Overview
|
||||||
|
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Real usage examples
|
||||||
|
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Error prevention
|
||||||
1110
docs/skills/n8n-automation/javascript-code/COMMON_PATTERNS.md
Normal file
1110
docs/skills/n8n-automation/javascript-code/COMMON_PATTERNS.md
Normal file
File diff suppressed because it is too large
Load Diff
782
docs/skills/n8n-automation/javascript-code/DATA_ACCESS.md
Normal file
782
docs/skills/n8n-automation/javascript-code/DATA_ACCESS.md
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
# Data Access Patterns - JavaScript Code Node
|
||||||
|
|
||||||
|
Comprehensive guide to accessing data in n8n Code nodes using JavaScript.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
In n8n Code nodes, you access data from previous nodes using built-in variables and methods. Understanding which method to use is critical for correct workflow execution.
|
||||||
|
|
||||||
|
**Data Access Priority** (by common usage):
|
||||||
|
1. **`$input.all()`** - Most common - Batch operations, aggregations
|
||||||
|
2. **`$input.first()`** - Very common - Single item operations
|
||||||
|
3. **`$input.item`** - Common - Each Item mode only
|
||||||
|
4. **`$node["NodeName"].json`** - Specific node references
|
||||||
|
5. **`$json`** - Direct current item (legacy, use `$input` instead)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 1: $input.all() - Process All Items
|
||||||
|
|
||||||
|
**Usage**: Most common pattern for batch processing
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Processing multiple records
|
||||||
|
- Aggregating data (sum, count, average)
|
||||||
|
- Filtering arrays
|
||||||
|
- Transforming datasets
|
||||||
|
- Comparing items
|
||||||
|
- Sorting or ranking
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get all items from previous node
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// allItems is an array of objects like:
|
||||||
|
// [
|
||||||
|
// {json: {id: 1, name: "Alice"}},
|
||||||
|
// {json: {id: 2, name: "Bob"}}
|
||||||
|
// ]
|
||||||
|
|
||||||
|
console.log(`Received ${allItems.length} items`);
|
||||||
|
|
||||||
|
return allItems;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 1: Filter Active Items
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Filter only active items
|
||||||
|
const activeItems = allItems.filter(item => item.json.status === 'active');
|
||||||
|
|
||||||
|
return activeItems;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Transform All Items
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Map to new structure
|
||||||
|
const transformed = allItems.map(item => ({
|
||||||
|
json: {
|
||||||
|
id: item.json.id,
|
||||||
|
fullName: `${item.json.firstName} ${item.json.lastName}`,
|
||||||
|
email: item.json.email,
|
||||||
|
processedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Aggregate Data
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Calculate total
|
||||||
|
const total = allItems.reduce((sum, item) => {
|
||||||
|
return sum + (item.json.amount || 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
total,
|
||||||
|
count: allItems.length,
|
||||||
|
average: total / allItems.length
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Sort and Limit
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Get top 5 by score
|
||||||
|
const topFive = allItems
|
||||||
|
.sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
|
||||||
|
.slice(0, 5);
|
||||||
|
|
||||||
|
return topFive.map(item => ({json: item.json}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 5: Group By Category
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Group items by category
|
||||||
|
const grouped = {};
|
||||||
|
|
||||||
|
for (const item of allItems) {
|
||||||
|
const category = item.json.category || 'Uncategorized';
|
||||||
|
|
||||||
|
if (!grouped[category]) {
|
||||||
|
grouped[category] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
grouped[category].push(item.json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to array format
|
||||||
|
return Object.entries(grouped).map(([category, items]) => ({
|
||||||
|
json: {
|
||||||
|
category,
|
||||||
|
items,
|
||||||
|
count: items.length
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 6: Deduplicate by ID
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Remove duplicates by ID
|
||||||
|
const seen = new Set();
|
||||||
|
const unique = [];
|
||||||
|
|
||||||
|
for (const item of allItems) {
|
||||||
|
const id = item.json.id;
|
||||||
|
|
||||||
|
if (!seen.has(id)) {
|
||||||
|
seen.add(id);
|
||||||
|
unique.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unique;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 2: $input.first() - Get First Item
|
||||||
|
|
||||||
|
**Usage**: Very common for single-item operations
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Previous node returns single object
|
||||||
|
- Working with API responses
|
||||||
|
- Getting initial/first data point
|
||||||
|
- Configuration or metadata access
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get first item from previous node
|
||||||
|
const firstItem = $input.first();
|
||||||
|
|
||||||
|
// Access the JSON data
|
||||||
|
const data = firstItem.json;
|
||||||
|
|
||||||
|
console.log('First item:', data);
|
||||||
|
|
||||||
|
return [{json: data}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 1: Process Single API Response
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get API response (typically single object)
|
||||||
|
const response = $input.first().json;
|
||||||
|
|
||||||
|
// Extract what you need
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
userId: response.data.user.id,
|
||||||
|
userName: response.data.user.name,
|
||||||
|
status: response.status,
|
||||||
|
fetchedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Transform Single Object
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const data = $input.first().json;
|
||||||
|
|
||||||
|
// Transform structure
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
id: data.id,
|
||||||
|
contact: {
|
||||||
|
email: data.email,
|
||||||
|
phone: data.phone
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
street: data.street,
|
||||||
|
city: data.city,
|
||||||
|
zip: data.zip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Validate Single Item
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const item = $input.first().json;
|
||||||
|
|
||||||
|
// Validation logic
|
||||||
|
const isValid = item.email && item.email.includes('@');
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...item,
|
||||||
|
valid: isValid,
|
||||||
|
validatedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Extract Nested Data
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const response = $input.first().json;
|
||||||
|
|
||||||
|
// Navigate nested structure
|
||||||
|
const users = response.data?.users || [];
|
||||||
|
|
||||||
|
return users.map(user => ({
|
||||||
|
json: {
|
||||||
|
id: user.id,
|
||||||
|
name: user.profile?.name || 'Unknown',
|
||||||
|
email: user.contact?.email || 'no-email'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 5: Combine with Other Methods
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get first item's data
|
||||||
|
const firstData = $input.first().json;
|
||||||
|
|
||||||
|
// Use it to filter all items
|
||||||
|
const allItems = $input.all();
|
||||||
|
const matching = allItems.filter(item =>
|
||||||
|
item.json.category === firstData.targetCategory
|
||||||
|
);
|
||||||
|
|
||||||
|
return matching;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 3: $input.item - Current Item (Each Item Mode)
|
||||||
|
|
||||||
|
**Usage**: Common in "Run Once for Each Item" mode
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Mode is set to "Run Once for Each Item"
|
||||||
|
- Need to process items independently
|
||||||
|
- Per-item API calls or validations
|
||||||
|
- Item-specific error handling
|
||||||
|
|
||||||
|
**IMPORTANT**: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// In "Run Once for Each Item" mode
|
||||||
|
const currentItem = $input.item;
|
||||||
|
const data = currentItem.json;
|
||||||
|
|
||||||
|
console.log('Processing item:', data.id);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...data,
|
||||||
|
processed: true
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 1: Add Processing Metadata
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const item = $input.item;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
processed: true,
|
||||||
|
processedAt: new Date().toISOString(),
|
||||||
|
processingDuration: Math.random() * 1000 // Simulated duration
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Per-Item Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const item = $input.item;
|
||||||
|
const data = item.json;
|
||||||
|
|
||||||
|
// Validate this specific item
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (!data.email) errors.push('Email required');
|
||||||
|
if (!data.name) errors.push('Name required');
|
||||||
|
if (data.age && data.age < 18) errors.push('Must be 18+');
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...data,
|
||||||
|
valid: errors.length === 0,
|
||||||
|
errors: errors.length > 0 ? errors : undefined
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Item-Specific API Call
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const item = $input.item;
|
||||||
|
const userId = item.json.userId;
|
||||||
|
|
||||||
|
// Make API call specific to this item
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: `https://api.example.com/users/${userId}/details`
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
details: response
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Conditional Processing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const item = $input.item;
|
||||||
|
const data = item.json;
|
||||||
|
|
||||||
|
// Process based on item type
|
||||||
|
if (data.type === 'premium') {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...data,
|
||||||
|
discount: 0.20,
|
||||||
|
tier: 'premium'
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...data,
|
||||||
|
discount: 0.05,
|
||||||
|
tier: 'standard'
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 4: $node - Reference Other Nodes
|
||||||
|
|
||||||
|
**Usage**: Less common, but powerful for specific scenarios
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Need data from specific named node
|
||||||
|
- Combining data from multiple nodes
|
||||||
|
- Accessing metadata about workflow execution
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get output from specific node
|
||||||
|
const webhookData = $node["Webhook"].json;
|
||||||
|
const apiData = $node["HTTP Request"].json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
fromWebhook: webhookData,
|
||||||
|
fromAPI: apiData
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 1: Combine Multiple Sources
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Reference multiple nodes
|
||||||
|
const webhook = $node["Webhook"].json;
|
||||||
|
const database = $node["Postgres"].json;
|
||||||
|
const api = $node["HTTP Request"].json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
combined: {
|
||||||
|
webhook: webhook.body,
|
||||||
|
dbRecords: database.length,
|
||||||
|
apiResponse: api.status
|
||||||
|
},
|
||||||
|
processedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Compare Across Nodes
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const oldData = $node["Get Old Data"].json;
|
||||||
|
const newData = $node["Get New Data"].json;
|
||||||
|
|
||||||
|
// Compare
|
||||||
|
const changes = {
|
||||||
|
added: newData.filter(n => !oldData.find(o => o.id === n.id)),
|
||||||
|
removed: oldData.filter(o => !newData.find(n => n.id === o.id)),
|
||||||
|
modified: newData.filter(n => {
|
||||||
|
const old = oldData.find(o => o.id === n.id);
|
||||||
|
return old && JSON.stringify(old) !== JSON.stringify(n);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
changes,
|
||||||
|
summary: {
|
||||||
|
added: changes.added.length,
|
||||||
|
removed: changes.removed.length,
|
||||||
|
modified: changes.modified.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Access Node Metadata
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get data from specific execution path
|
||||||
|
const ifTrueBranch = $node["IF True"].json;
|
||||||
|
const ifFalseBranch = $node["IF False"].json;
|
||||||
|
|
||||||
|
// Use whichever branch executed
|
||||||
|
const result = ifTrueBranch || ifFalseBranch || {};
|
||||||
|
|
||||||
|
return [{json: result}];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critical: Webhook Data Structure
|
||||||
|
|
||||||
|
**MOST COMMON MISTAKE**: Forgetting webhook data is nested under `.body`
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
Webhook node wraps all incoming data under a `body` property. This catches many developers by surprise.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Webhook node output structure:
|
||||||
|
{
|
||||||
|
"headers": {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"user-agent": "...",
|
||||||
|
// ... other headers
|
||||||
|
},
|
||||||
|
"params": {},
|
||||||
|
"query": {},
|
||||||
|
"body": {
|
||||||
|
// ← YOUR DATA IS HERE
|
||||||
|
"name": "Alice",
|
||||||
|
"email": "alice@example.com",
|
||||||
|
"message": "Hello!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wrong vs Right
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Trying to access directly
|
||||||
|
const name = $json.name; // undefined
|
||||||
|
const email = $json.email; // undefined
|
||||||
|
|
||||||
|
// ✅ CORRECT: Access via .body
|
||||||
|
const name = $json.body.name; // "Alice"
|
||||||
|
const email = $json.body.email; // "alice@example.com"
|
||||||
|
|
||||||
|
// ✅ CORRECT: Extract body first
|
||||||
|
const webhookData = $json.body;
|
||||||
|
const name = webhookData.name; // "Alice"
|
||||||
|
const email = webhookData.email; // "alice@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Full Webhook Processing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get webhook data from previous node
|
||||||
|
const webhookOutput = $input.first().json;
|
||||||
|
|
||||||
|
// Access the actual payload
|
||||||
|
const payload = webhookOutput.body;
|
||||||
|
|
||||||
|
// Access headers if needed
|
||||||
|
const contentType = webhookOutput.headers['content-type'];
|
||||||
|
|
||||||
|
// Access query parameters if needed
|
||||||
|
const apiKey = webhookOutput.query.api_key;
|
||||||
|
|
||||||
|
// Process the actual data
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// Data from webhook body
|
||||||
|
userName: payload.name,
|
||||||
|
userEmail: payload.email,
|
||||||
|
message: payload.message,
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
receivedAt: new Date().toISOString(),
|
||||||
|
contentType: contentType,
|
||||||
|
authenticated: !!apiKey
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST Data, Query Params, and Headers
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const webhook = $input.first().json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
// POST body data
|
||||||
|
formData: webhook.body,
|
||||||
|
|
||||||
|
// Query parameters (?key=value)
|
||||||
|
queryParams: webhook.query,
|
||||||
|
|
||||||
|
// HTTP headers
|
||||||
|
userAgent: webhook.headers['user-agent'],
|
||||||
|
contentType: webhook.headers['content-type'],
|
||||||
|
|
||||||
|
// Request metadata
|
||||||
|
method: webhook.method, // POST, GET, etc.
|
||||||
|
url: webhook.url
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Webhook Scenarios
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Scenario 1: Form submission
|
||||||
|
const formData = $json.body;
|
||||||
|
const name = formData.name;
|
||||||
|
const email = formData.email;
|
||||||
|
|
||||||
|
// Scenario 2: JSON API webhook
|
||||||
|
const apiPayload = $json.body;
|
||||||
|
const eventType = apiPayload.event;
|
||||||
|
const data = apiPayload.data;
|
||||||
|
|
||||||
|
// Scenario 3: Query parameters
|
||||||
|
const apiKey = $json.query.api_key;
|
||||||
|
const userId = $json.query.user_id;
|
||||||
|
|
||||||
|
// Scenario 4: Headers
|
||||||
|
const authorization = $json.headers['authorization'];
|
||||||
|
const signature = $json.headers['x-signature'];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Choosing the Right Pattern
|
||||||
|
|
||||||
|
### Decision Tree
|
||||||
|
|
||||||
|
```
|
||||||
|
Do you need ALL items from previous node?
|
||||||
|
├─ YES → Use $input.all()
|
||||||
|
│
|
||||||
|
└─ NO → Do you need just the FIRST item?
|
||||||
|
├─ YES → Use $input.first()
|
||||||
|
│
|
||||||
|
└─ NO → Are you in "Each Item" mode?
|
||||||
|
├─ YES → Use $input.item
|
||||||
|
│
|
||||||
|
└─ NO → Do you need specific node data?
|
||||||
|
├─ YES → Use $node["NodeName"]
|
||||||
|
└─ NO → Use $input.first() (default)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quick Reference Table
|
||||||
|
|
||||||
|
| Scenario | Use This | Example |
|
||||||
|
|----------|----------|---------|
|
||||||
|
| Sum all amounts | `$input.all()` | `allItems.reduce((sum, i) => sum + i.json.amount, 0)` |
|
||||||
|
| Get API response | `$input.first()` | `$input.first().json.data` |
|
||||||
|
| Process each independently | `$input.item` | `$input.item.json` (Each Item mode) |
|
||||||
|
| Combine two nodes | `$node["Name"]` | `$node["API"].json` |
|
||||||
|
| Filter array | `$input.all()` | `allItems.filter(i => i.json.active)` |
|
||||||
|
| Transform single object | `$input.first()` | `{...input.first().json, new: true}` |
|
||||||
|
| Webhook data | `$input.first()` | `$input.first().json.body` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Mistakes
|
||||||
|
|
||||||
|
### Mistake 1: Using $json Without Context
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: $json is ambiguous
|
||||||
|
const value = $json.field;
|
||||||
|
|
||||||
|
// ✅ CORRECT: Be explicit
|
||||||
|
const value = $input.first().json.field;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mistake 2: Forgetting .json Property
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Trying to access fields on item object
|
||||||
|
const items = $input.all();
|
||||||
|
const names = items.map(item => item.name); // undefined
|
||||||
|
|
||||||
|
// ✅ CORRECT: Access via .json
|
||||||
|
const names = items.map(item => item.json.name);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mistake 3: Using $input.item in All Items Mode
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: $input.item is undefined in "All Items" mode
|
||||||
|
const data = $input.item.json; // Error!
|
||||||
|
|
||||||
|
// ✅ CORRECT: Use appropriate method
|
||||||
|
const data = $input.first().json; // Or $input.all()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mistake 4: Not Handling Empty Arrays
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Crashes if no items
|
||||||
|
const first = $input.all()[0].json;
|
||||||
|
|
||||||
|
// ✅ CORRECT: Check length first
|
||||||
|
const items = $input.all();
|
||||||
|
if (items.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const first = items[0].json;
|
||||||
|
|
||||||
|
// ✅ ALSO CORRECT: Use $input.first()
|
||||||
|
const first = $input.first().json; // Built-in safety
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mistake 5: Modifying Original Data
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ RISKY: Mutating original
|
||||||
|
const items = $input.all();
|
||||||
|
items[0].json.modified = true; // Modifies original
|
||||||
|
return items;
|
||||||
|
|
||||||
|
// ✅ SAFE: Create new objects
|
||||||
|
const items = $input.all();
|
||||||
|
return items.map(item => ({
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
modified: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Pattern: Pagination Handling
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const currentPage = $input.all();
|
||||||
|
const pageNumber = $node["Set Page"].json.page || 1;
|
||||||
|
|
||||||
|
// Combine with previous pages
|
||||||
|
const allPreviousPages = $node["Accumulator"]?.json.accumulated || [];
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
accumulated: [...allPreviousPages, ...currentPage],
|
||||||
|
currentPage: pageNumber,
|
||||||
|
totalItems: allPreviousPages.length + currentPage.length
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: Conditional Node Reference
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Access different nodes based on condition
|
||||||
|
const condition = $input.first().json.type;
|
||||||
|
|
||||||
|
let data;
|
||||||
|
if (condition === 'api') {
|
||||||
|
data = $node["API Response"].json;
|
||||||
|
} else if (condition === 'database') {
|
||||||
|
data = $node["Database"].json;
|
||||||
|
} else {
|
||||||
|
data = $node["Default"].json;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{json: data}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: Multi-Node Aggregation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Collect data from multiple named nodes
|
||||||
|
const sources = ['Source1', 'Source2', 'Source3'];
|
||||||
|
const allData = [];
|
||||||
|
|
||||||
|
for (const source of sources) {
|
||||||
|
const nodeData = $node[source]?.json;
|
||||||
|
if (nodeData) {
|
||||||
|
allData.push({
|
||||||
|
source,
|
||||||
|
data: nodeData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allData.map(item => ({json: item}));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Most Common Patterns**:
|
||||||
|
1. `$input.all()` - Process multiple items, batch operations
|
||||||
|
2. `$input.first()` - Single item, API responses
|
||||||
|
3. `$input.item` - Each Item mode processing
|
||||||
|
|
||||||
|
**Critical Rule**:
|
||||||
|
- Webhook data is under `.body` property
|
||||||
|
|
||||||
|
**Best Practice**:
|
||||||
|
- Be explicit: Use `$input.first().json.field` instead of `$json.field`
|
||||||
|
- Always check for null/undefined
|
||||||
|
- Use appropriate method for your mode (All Items vs Each Item)
|
||||||
|
|
||||||
|
**See Also**:
|
||||||
|
- [SKILL.md](SKILL.md) - Overview and quick start
|
||||||
|
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Production patterns
|
||||||
|
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes
|
||||||
763
docs/skills/n8n-automation/javascript-code/ERROR_PATTERNS.md
Normal file
763
docs/skills/n8n-automation/javascript-code/ERROR_PATTERNS.md
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
# Error Patterns - JavaScript Code Node
|
||||||
|
|
||||||
|
Complete guide to avoiding the most common Code node errors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers the **top 5 error patterns** encountered in n8n Code nodes. Understanding and avoiding these errors will save you significant debugging time.
|
||||||
|
|
||||||
|
**Error Frequency**:
|
||||||
|
1. Empty Code / Missing Return - **38% of failures**
|
||||||
|
2. Expression Syntax Confusion - **8% of failures**
|
||||||
|
3. Incorrect Return Wrapper - **5% of failures**
|
||||||
|
4. Unmatched Expression Brackets - **6% of failures**
|
||||||
|
5. Missing Null Checks - **Common runtime error**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error #1: Empty Code or Missing Return Statement
|
||||||
|
|
||||||
|
**Frequency**: Most common error (38% of all validation failures)
|
||||||
|
|
||||||
|
**What Happens**:
|
||||||
|
- Workflow execution fails
|
||||||
|
- Next nodes receive no data
|
||||||
|
- Error: "Code cannot be empty" or "Code must return data"
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ ERROR: No code at all
|
||||||
|
// (Empty code field)
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ ERROR: Code executes but doesn't return anything
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
// Process items
|
||||||
|
for (const item of items) {
|
||||||
|
console.log(item.json.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forgot to return!
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ ERROR: Early return path exists, but not all paths return
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return []; // ✅ This path returns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process items
|
||||||
|
const processed = items.map(item => ({json: item.json}));
|
||||||
|
|
||||||
|
// ❌ Forgot to return processed!
|
||||||
|
```
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Always return data
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
// Process items
|
||||||
|
const processed = items.map(item => ({
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
processed: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return processed; // ✅ Return statement present
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Return empty array if no items
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return []; // Valid: empty array when no data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process and return
|
||||||
|
return items.map(item => ({json: item.json}));
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: All code paths return
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return [];
|
||||||
|
} else if (items.length === 1) {
|
||||||
|
return [{json: {single: true, data: items[0].json}}];
|
||||||
|
} else {
|
||||||
|
return items.map(item => ({json: item.json}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// All paths covered
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Code field is not empty
|
||||||
|
- [ ] Return statement exists
|
||||||
|
- [ ] ALL code paths return data (if/else branches)
|
||||||
|
- [ ] Return format is correct (`[{json: {...}}]`)
|
||||||
|
- [ ] Return happens even on errors (use try-catch)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error #2: Expression Syntax Confusion
|
||||||
|
|
||||||
|
**Frequency**: 8% of validation failures
|
||||||
|
|
||||||
|
**What Happens**:
|
||||||
|
- Syntax error in code execution
|
||||||
|
- Error: "Unexpected token" or "Expression syntax is not valid in Code nodes"
|
||||||
|
- Template variables not evaluated
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
n8n has TWO distinct syntaxes:
|
||||||
|
1. **Expression syntax** `{{ }}` - Used in OTHER nodes (Set, IF, HTTP Request)
|
||||||
|
2. **JavaScript** - Used in CODE nodes (no `{{ }}`)
|
||||||
|
|
||||||
|
Many developers mistakenly use expression syntax inside Code nodes.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Using n8n expression syntax in Code node
|
||||||
|
const userName = "{{ $json.name }}";
|
||||||
|
const userEmail = "{{ $json.body.email }}";
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
name: userName,
|
||||||
|
email: userEmail
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Result: Literal string "{{ $json.name }}", NOT the value!
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Trying to evaluate expressions
|
||||||
|
const value = "{{ $now.toFormat('yyyy-MM-dd') }}";
|
||||||
|
```
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Use JavaScript directly (no {{ }})
|
||||||
|
const userName = $json.name;
|
||||||
|
const userEmail = $json.body.email;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
name: userName,
|
||||||
|
email: userEmail
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: JavaScript template literals (use backticks)
|
||||||
|
const message = `Hello, ${$json.name}! Your email is ${$json.email}`;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
greeting: message
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Direct variable access
|
||||||
|
const item = $input.first().json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
name: item.name,
|
||||||
|
email: item.email,
|
||||||
|
timestamp: new Date().toISOString() // JavaScript Date, not {{ }}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comparison Table
|
||||||
|
|
||||||
|
| Context | Syntax | Example |
|
||||||
|
|---------|--------|---------|
|
||||||
|
| Set node | `{{ }}` expressions | `{{ $json.name }}` |
|
||||||
|
| IF node | `{{ }}` expressions | `{{ $json.age > 18 }}` |
|
||||||
|
| HTTP Request URL | `{{ }}` expressions | `{{ $json.userId }}` |
|
||||||
|
| **Code node** | **JavaScript** | `$json.name` |
|
||||||
|
| **Code node strings** | **Template literals** | `` `Hello ${$json.name}` `` |
|
||||||
|
|
||||||
|
### Quick Fix Guide
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// WRONG → RIGHT conversions
|
||||||
|
|
||||||
|
// ❌ "{{ $json.field }}"
|
||||||
|
// ✅ $json.field
|
||||||
|
|
||||||
|
// ❌ "{{ $now }}"
|
||||||
|
// ✅ new Date().toISOString()
|
||||||
|
|
||||||
|
// ❌ "{{ $node['HTTP Request'].json.data }}"
|
||||||
|
// ✅ $node["HTTP Request"].json.data
|
||||||
|
|
||||||
|
// ❌ `{{ $json.firstName }} {{ $json.lastName }}`
|
||||||
|
// ✅ `${$json.firstName} ${$json.lastName}`
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error #3: Incorrect Return Wrapper Format
|
||||||
|
|
||||||
|
**Frequency**: 5% of validation failures
|
||||||
|
|
||||||
|
**What Happens**:
|
||||||
|
- Error: "Return value must be an array of objects"
|
||||||
|
- Error: "Each item must have a json property"
|
||||||
|
- Next nodes receive malformed data
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
Code nodes MUST return:
|
||||||
|
- **Array** of objects
|
||||||
|
- Each object MUST have a **`json` property**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Returning object instead of array
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
result: 'success'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Missing array wrapper []
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Returning array without json wrapper
|
||||||
|
return [
|
||||||
|
{id: 1, name: 'Alice'},
|
||||||
|
{id: 2, name: 'Bob'}
|
||||||
|
];
|
||||||
|
// Missing json property
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Returning plain value
|
||||||
|
return "processed";
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Returning items without mapping
|
||||||
|
return $input.all();
|
||||||
|
// Works if items already have json property, but not guaranteed
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Incomplete structure
|
||||||
|
return [{data: {result: 'success'}}];
|
||||||
|
// Should be {json: {...}}, not {data: {...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Single result
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
result: 'success',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Multiple results
|
||||||
|
return [
|
||||||
|
{json: {id: 1, name: 'Alice'}},
|
||||||
|
{json: {id: 2, name: 'Bob'}},
|
||||||
|
{json: {id: 3, name: 'Carol'}}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Transforming array
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
return items.map(item => ({
|
||||||
|
json: {
|
||||||
|
id: item.json.id,
|
||||||
|
name: item.json.name,
|
||||||
|
processed: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Empty result
|
||||||
|
return [];
|
||||||
|
// Valid when no data to return
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Conditional returns
|
||||||
|
if (shouldProcess) {
|
||||||
|
return [{json: {result: 'processed'}}];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Return Format Checklist
|
||||||
|
|
||||||
|
- [ ] Return value is an **array** `[...]`
|
||||||
|
- [ ] Each array element has **`json` property**
|
||||||
|
- [ ] Structure is `[{json: {...}}]` or `[{json: {...}}, {json: {...}}]`
|
||||||
|
- [ ] NOT `{json: {...}}` (missing array wrapper)
|
||||||
|
- [ ] NOT `[{...}]` (missing json property)
|
||||||
|
|
||||||
|
### Common Scenarios
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Scenario 1: Single object from API
|
||||||
|
const response = $input.first().json;
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
return [{json: response}];
|
||||||
|
|
||||||
|
// ❌ WRONG
|
||||||
|
return {json: response};
|
||||||
|
|
||||||
|
|
||||||
|
// Scenario 2: Array of objects
|
||||||
|
const users = $input.all();
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
return users.map(user => ({json: user.json}));
|
||||||
|
|
||||||
|
// ❌ WRONG
|
||||||
|
return users; // Risky - depends on existing structure
|
||||||
|
|
||||||
|
|
||||||
|
// Scenario 3: Computed result
|
||||||
|
const total = $input.all().reduce((sum, item) => sum + item.json.amount, 0);
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
return [{json: {total}}];
|
||||||
|
|
||||||
|
// ❌ WRONG
|
||||||
|
return {total};
|
||||||
|
|
||||||
|
|
||||||
|
// Scenario 4: No results
|
||||||
|
// ✅ CORRECT
|
||||||
|
return [];
|
||||||
|
|
||||||
|
// ❌ WRONG
|
||||||
|
return null;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error #4: Unmatched Expression Brackets
|
||||||
|
|
||||||
|
**Frequency**: 6% of validation failures
|
||||||
|
|
||||||
|
**What Happens**:
|
||||||
|
- Parsing error during save
|
||||||
|
- Error: "Unmatched expression brackets"
|
||||||
|
- Code appears correct but fails validation
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
This error typically occurs when:
|
||||||
|
1. Strings contain unbalanced quotes
|
||||||
|
2. Multi-line strings with special characters
|
||||||
|
3. Template literals with nested brackets
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Unescaped quote in string
|
||||||
|
const message = "It's a nice day";
|
||||||
|
// Single quote breaks string
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Unbalanced brackets in regex
|
||||||
|
const pattern = /\{(\w+)\}/; // JSON storage issue
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Multi-line string with quotes
|
||||||
|
const html = "
|
||||||
|
<div class="container">
|
||||||
|
<p>Hello</p>
|
||||||
|
</div>
|
||||||
|
";
|
||||||
|
// Quote balance issues
|
||||||
|
```
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Escape quotes
|
||||||
|
const message = "It\\'s a nice day";
|
||||||
|
// Or use different quotes
|
||||||
|
const message = "It's a nice day"; // Double quotes work
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Escape regex properly
|
||||||
|
const pattern = /\\{(\\w+)\\}/;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Template literals for multi-line
|
||||||
|
const html = `
|
||||||
|
<div class="container">
|
||||||
|
<p>Hello</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
// Backticks handle multi-line and quotes
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Escape backslashes
|
||||||
|
const path = "C:\\\\Users\\\\Documents\\\\file.txt";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Escaping Guide
|
||||||
|
|
||||||
|
| Character | Escape As | Example |
|
||||||
|
|-----------|-----------|---------|
|
||||||
|
| Single quote in single-quoted string | `\\'` | `'It\\'s working'` |
|
||||||
|
| Double quote in double-quoted string | `\\"` | `"She said \\"hello\\""` |
|
||||||
|
| Backslash | `\\\\` | `"C:\\\\path"` |
|
||||||
|
| Newline | `\\n` | `"Line 1\\nLine 2"` |
|
||||||
|
| Tab | `\\t` | `"Column1\\tColumn2"` |
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ BEST: Use template literals for complex strings
|
||||||
|
const message = `User ${name} said: "Hello!"`;
|
||||||
|
|
||||||
|
// ✅ BEST: Use template literals for HTML
|
||||||
|
const html = `
|
||||||
|
<div class="${className}">
|
||||||
|
<h1>${title}</h1>
|
||||||
|
<p>${content}</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// ✅ BEST: Use template literals for JSON
|
||||||
|
const jsonString = `{
|
||||||
|
"name": "${name}",
|
||||||
|
"email": "${email}"
|
||||||
|
}`;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error #5: Missing Null Checks / Undefined Access
|
||||||
|
|
||||||
|
**Frequency**: Very common runtime error
|
||||||
|
|
||||||
|
**What Happens**:
|
||||||
|
- Workflow execution stops
|
||||||
|
- Error: "Cannot read property 'X' of undefined"
|
||||||
|
- Error: "Cannot read property 'X' of null"
|
||||||
|
- Crashes on missing data
|
||||||
|
|
||||||
|
### The Problem
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: No null check - crashes if user doesn't exist
|
||||||
|
const email = item.json.user.email;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Assumes array has items
|
||||||
|
const firstItem = $input.all()[0].json;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Assumes nested property exists
|
||||||
|
const city = $json.address.city;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: No validation before array operations
|
||||||
|
const names = $json.users.map(user => user.name);
|
||||||
|
```
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Optional chaining
|
||||||
|
const email = item.json?.user?.email || 'no-email@example.com';
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Check array length
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstItem = items[0].json;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Guard clauses
|
||||||
|
const data = $input.first().json;
|
||||||
|
|
||||||
|
if (!data.address) {
|
||||||
|
return [{json: {error: 'No address provided'}}];
|
||||||
|
}
|
||||||
|
|
||||||
|
const city = data.address.city;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Default values
|
||||||
|
const users = $json.users || [];
|
||||||
|
const names = users.map(user => user.name || 'Unknown');
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT: Try-catch for risky operations
|
||||||
|
try {
|
||||||
|
const email = item.json.user.email.toLowerCase();
|
||||||
|
return [{json: {email}}];
|
||||||
|
} catch (error) {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
error: 'Invalid user data',
|
||||||
|
details: error.message
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Safe Access Patterns
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Pattern 1: Optional chaining (modern, recommended)
|
||||||
|
const value = data?.nested?.property?.value;
|
||||||
|
|
||||||
|
// Pattern 2: Logical OR with default
|
||||||
|
const value = data.property || 'default';
|
||||||
|
|
||||||
|
// Pattern 3: Ternary check
|
||||||
|
const value = data.property ? data.property : 'default';
|
||||||
|
|
||||||
|
// Pattern 4: Guard clause
|
||||||
|
if (!data.property) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const value = data.property;
|
||||||
|
|
||||||
|
// Pattern 5: Try-catch
|
||||||
|
try {
|
||||||
|
const value = data.nested.property.value;
|
||||||
|
} catch (error) {
|
||||||
|
const value = 'default';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Webhook Data Safety
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Webhook data requires extra safety
|
||||||
|
|
||||||
|
// ❌ RISKY: Assumes all fields exist
|
||||||
|
const name = $json.body.user.name;
|
||||||
|
const email = $json.body.user.email;
|
||||||
|
|
||||||
|
// ✅ SAFE: Check each level
|
||||||
|
const body = $json.body || {};
|
||||||
|
const user = body.user || {};
|
||||||
|
const name = user.name || 'Unknown';
|
||||||
|
const email = user.email || 'no-email';
|
||||||
|
|
||||||
|
// ✅ BETTER: Optional chaining
|
||||||
|
const name = $json.body?.user?.name || 'Unknown';
|
||||||
|
const email = $json.body?.user?.email || 'no-email';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array Safety
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ RISKY: No length check
|
||||||
|
const items = $input.all();
|
||||||
|
const firstId = items[0].json.id;
|
||||||
|
|
||||||
|
// ✅ SAFE: Check length
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
const firstId = items[0].json.id;
|
||||||
|
} else {
|
||||||
|
// Handle empty case
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ BETTER: Use $input.first()
|
||||||
|
const firstItem = $input.first();
|
||||||
|
const firstId = firstItem.json.id; // Built-in safety
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Property Safety
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ RISKY: Direct access
|
||||||
|
const config = $json.settings.advanced.timeout;
|
||||||
|
|
||||||
|
// ✅ SAFE: Step by step with defaults
|
||||||
|
const settings = $json.settings || {};
|
||||||
|
const advanced = settings.advanced || {};
|
||||||
|
const timeout = advanced.timeout || 30000;
|
||||||
|
|
||||||
|
// ✅ BETTER: Optional chaining
|
||||||
|
const timeout = $json.settings?.advanced?.timeout ?? 30000;
|
||||||
|
// Note: ?? (nullish coalescing) vs || (logical OR)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Prevention Checklist
|
||||||
|
|
||||||
|
Use this checklist before deploying Code nodes:
|
||||||
|
|
||||||
|
### Code Structure
|
||||||
|
- [ ] Code field is not empty
|
||||||
|
- [ ] Return statement exists
|
||||||
|
- [ ] All code paths return data
|
||||||
|
|
||||||
|
### Return Format
|
||||||
|
- [ ] Returns array: `[...]`
|
||||||
|
- [ ] Each item has `json` property: `{json: {...}}`
|
||||||
|
- [ ] Format is `[{json: {...}}]`
|
||||||
|
|
||||||
|
### Syntax
|
||||||
|
- [ ] No `{{ }}` expression syntax (use JavaScript)
|
||||||
|
- [ ] Template literals use backticks: `` `${variable}` ``
|
||||||
|
- [ ] All quotes and brackets balanced
|
||||||
|
- [ ] Strings properly escaped
|
||||||
|
|
||||||
|
### Data Safety
|
||||||
|
- [ ] Null checks for optional properties
|
||||||
|
- [ ] Array length checks before access
|
||||||
|
- [ ] Webhook data accessed via `.body`
|
||||||
|
- [ ] Try-catch for risky operations
|
||||||
|
- [ ] Default values for missing data
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- [ ] Test with empty input
|
||||||
|
- [ ] Test with missing fields
|
||||||
|
- [ ] Test with unexpected data types
|
||||||
|
- [ ] Check browser console for errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Error Reference
|
||||||
|
|
||||||
|
| Error Message | Likely Cause | Fix |
|
||||||
|
|---------------|--------------|-----|
|
||||||
|
| "Code cannot be empty" | Empty code field | Add meaningful code |
|
||||||
|
| "Code must return data" | Missing return statement | Add `return [...]` |
|
||||||
|
| "Return value must be an array" | Returning object instead of array | Wrap in `[...]` |
|
||||||
|
| "Each item must have json property" | Missing `json` wrapper | Use `{json: {...}}` |
|
||||||
|
| "Unexpected token" | Expression syntax `{{ }}` in code | Remove `{{ }}`, use JavaScript |
|
||||||
|
| "Cannot read property X of undefined" | Missing null check | Use optional chaining `?.` |
|
||||||
|
| "Cannot read property X of null" | Null value access | Add guard clause or default |
|
||||||
|
| "Unmatched expression brackets" | Quote/bracket imbalance | Check string escaping |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debugging Tips
|
||||||
|
|
||||||
|
### 1. Use console.log()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
console.log('Items count:', items.length);
|
||||||
|
console.log('First item:', items[0]);
|
||||||
|
|
||||||
|
// Check browser console (F12) for output
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Return Intermediate Results
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Debug by returning current state
|
||||||
|
const items = $input.all();
|
||||||
|
const processed = items.map(item => ({json: item.json}));
|
||||||
|
|
||||||
|
// Return to see what you have
|
||||||
|
return processed;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Try-Catch for Troubleshooting
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
try {
|
||||||
|
// Your code here
|
||||||
|
const result = riskyOperation();
|
||||||
|
return [{json: {result}}];
|
||||||
|
} catch (error) {
|
||||||
|
// See what failed
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Validate Input Structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
// Check what you received
|
||||||
|
console.log('Input structure:', JSON.stringify(items[0], null, 2));
|
||||||
|
|
||||||
|
// Then process
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Top 5 Errors to Avoid**:
|
||||||
|
1. **Empty code / missing return** (38%) - Always return data
|
||||||
|
2. **Expression syntax `{{ }}`** (8%) - Use JavaScript, not expressions
|
||||||
|
3. **Wrong return format** (5%) - Always `[{json: {...}}]`
|
||||||
|
4. **Unmatched brackets** (6%) - Escape strings properly
|
||||||
|
5. **Missing null checks** - Use optional chaining `?.`
|
||||||
|
|
||||||
|
**Quick Prevention**:
|
||||||
|
- Return `[{json: {...}}]` format
|
||||||
|
- Use JavaScript, NOT `{{ }}` expressions
|
||||||
|
- Check for null/undefined before accessing
|
||||||
|
- Test with empty and invalid data
|
||||||
|
- Use browser console for debugging
|
||||||
|
|
||||||
|
**See Also**:
|
||||||
|
- [SKILL.md](SKILL.md) - Overview and best practices
|
||||||
|
- [DATA_ACCESS.md](DATA_ACCESS.md) - Safe data access patterns
|
||||||
|
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Working examples
|
||||||
350
docs/skills/n8n-automation/javascript-code/README.md
Normal file
350
docs/skills/n8n-automation/javascript-code/README.md
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
# n8n Code JavaScript
|
||||||
|
|
||||||
|
Expert guidance for writing JavaScript code in n8n Code nodes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Teaches how to write effective JavaScript in n8n Code nodes, avoid common errors, and use built-in functions effectively.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Activates On
|
||||||
|
|
||||||
|
**Trigger keywords**:
|
||||||
|
- "javascript code node"
|
||||||
|
- "write javascript in n8n"
|
||||||
|
- "code node javascript"
|
||||||
|
- "$input syntax"
|
||||||
|
- "$json syntax"
|
||||||
|
- "$helpers.httpRequest"
|
||||||
|
- "DateTime luxon"
|
||||||
|
- "code node error"
|
||||||
|
- "webhook data code"
|
||||||
|
- "return format code node"
|
||||||
|
|
||||||
|
**Common scenarios**:
|
||||||
|
- Writing JavaScript code in Code nodes
|
||||||
|
- Troubleshooting Code node errors
|
||||||
|
- Making HTTP requests from code
|
||||||
|
- Working with dates and times
|
||||||
|
- Accessing webhook data
|
||||||
|
- Choosing between All Items and Each Item mode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What You'll Learn
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
- Mode selection (All Items vs Each Item)
|
||||||
|
- Data access patterns ($input.all(), $input.first(), $input.item)
|
||||||
|
- Correct return format: `[{json: {...}}]`
|
||||||
|
- Webhook data structure (.body nesting)
|
||||||
|
- Built-in functions overview
|
||||||
|
|
||||||
|
### Data Access Mastery
|
||||||
|
- $input.all() - Batch operations (most common)
|
||||||
|
- $input.first() - Single item operations
|
||||||
|
- $input.item - Each Item mode processing
|
||||||
|
- $node - Reference other workflow nodes
|
||||||
|
- **Critical gotcha**: Webhook data under `.body`
|
||||||
|
|
||||||
|
### Common Patterns (Production-Tested)
|
||||||
|
1. Multi-source Data Aggregation
|
||||||
|
2. Regex Filtering & Pattern Matching
|
||||||
|
3. Markdown Parsing & Structured Extraction
|
||||||
|
4. JSON Comparison & Validation
|
||||||
|
5. CRM Data Transformation
|
||||||
|
6. Release Information Processing
|
||||||
|
7. Array Transformation with Context
|
||||||
|
8. Slack Block Kit Formatting
|
||||||
|
9. Top N Filtering & Ranking
|
||||||
|
10. String Aggregation & Reporting
|
||||||
|
|
||||||
|
### Error Prevention
|
||||||
|
Top 5 errors to avoid:
|
||||||
|
1. **Empty code / missing return** (38% of failures)
|
||||||
|
2. **Expression syntax confusion** (using `{{}}` in code)
|
||||||
|
3. **Incorrect return format** (missing array wrapper or json property)
|
||||||
|
4. **Unmatched brackets** (string escaping issues)
|
||||||
|
5. **Missing null checks** (crashes on undefined)
|
||||||
|
|
||||||
|
### Built-in Functions
|
||||||
|
- **$helpers.httpRequest()** - Make HTTP requests
|
||||||
|
- **DateTime (Luxon)** - Advanced date/time operations
|
||||||
|
- **$jmespath()** - Query JSON structures
|
||||||
|
- **$getWorkflowStaticData()** - Persistent storage
|
||||||
|
- Standard JavaScript globals (Math, JSON, console)
|
||||||
|
- Available Node.js modules (crypto, Buffer, URL)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
n8n-code-javascript/
|
||||||
|
├── SKILL.md (500 lines)
|
||||||
|
│ Overview, quick start, mode selection, best practices
|
||||||
|
│ - Mode selection guide (All Items vs Each Item)
|
||||||
|
│ - Data access patterns overview
|
||||||
|
│ - Return format requirements
|
||||||
|
│ - Critical webhook gotcha
|
||||||
|
│ - Error prevention overview
|
||||||
|
│ - Quick reference checklist
|
||||||
|
│
|
||||||
|
├── DATA_ACCESS.md (400 lines)
|
||||||
|
│ Complete data access patterns
|
||||||
|
│ - $input.all() - Most common (26% usage)
|
||||||
|
│ - $input.first() - Very common (25% usage)
|
||||||
|
│ - $input.item - Each Item mode (19% usage)
|
||||||
|
│ - $node - Reference other nodes
|
||||||
|
│ - Webhook data structure (.body nesting)
|
||||||
|
│ - Choosing the right pattern
|
||||||
|
│ - Common mistakes to avoid
|
||||||
|
│
|
||||||
|
├── COMMON_PATTERNS.md (600 lines)
|
||||||
|
│ 10 production-tested patterns
|
||||||
|
│ - Pattern 1: Multi-source Aggregation
|
||||||
|
│ - Pattern 2: Regex Filtering
|
||||||
|
│ - Pattern 3: Markdown Parsing
|
||||||
|
│ - Pattern 4: JSON Comparison
|
||||||
|
│ - Pattern 5: CRM Transformation
|
||||||
|
│ - Pattern 6: Release Processing
|
||||||
|
│ - Pattern 7: Array Transformation
|
||||||
|
│ - Pattern 8: Slack Block Kit
|
||||||
|
│ - Pattern 9: Top N Filtering
|
||||||
|
│ - Pattern 10: String Aggregation
|
||||||
|
│ - Pattern selection guide
|
||||||
|
│
|
||||||
|
├── ERROR_PATTERNS.md (450 lines)
|
||||||
|
│ Top 5 errors with solutions
|
||||||
|
│ - Error #1: Empty Code / Missing Return (38%)
|
||||||
|
│ - Error #2: Expression Syntax Confusion (8%)
|
||||||
|
│ - Error #3: Incorrect Return Wrapper (5%)
|
||||||
|
│ - Error #4: Unmatched Brackets (6%)
|
||||||
|
│ - Error #5: Missing Null Checks
|
||||||
|
│ - Error prevention checklist
|
||||||
|
│ - Quick error reference
|
||||||
|
│ - Debugging tips
|
||||||
|
│
|
||||||
|
├── BUILTIN_FUNCTIONS.md (450 lines)
|
||||||
|
│ Complete built-in function reference
|
||||||
|
│ - $helpers.httpRequest() API reference
|
||||||
|
│ - DateTime (Luxon) complete guide
|
||||||
|
│ - $jmespath() JSON querying
|
||||||
|
│ - $getWorkflowStaticData() persistent storage
|
||||||
|
│ - Standard JavaScript globals
|
||||||
|
│ - Available Node.js modules
|
||||||
|
│ - What's NOT available
|
||||||
|
│
|
||||||
|
└── README.md (this file)
|
||||||
|
Skill metadata and overview
|
||||||
|
```
|
||||||
|
|
||||||
|
**Total**: ~2,400 lines across 6 files
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
### Mode Selection
|
||||||
|
- **Run Once for All Items** - Recommended for 95% of use cases
|
||||||
|
- **Run Once for Each Item** - Specialized cases only
|
||||||
|
- Decision guide and performance implications
|
||||||
|
|
||||||
|
### Data Access
|
||||||
|
- Most common patterns with usage statistics
|
||||||
|
- Webhook data structure (critical .body gotcha)
|
||||||
|
- Safe access patterns with null checks
|
||||||
|
- When to use which pattern
|
||||||
|
|
||||||
|
### Error Prevention
|
||||||
|
- Top 5 errors covering 62%+ of all failures
|
||||||
|
- Clear wrong vs right examples
|
||||||
|
- Error prevention checklist
|
||||||
|
- Debugging tips and console.log usage
|
||||||
|
|
||||||
|
### Production Patterns
|
||||||
|
- 10 patterns from real workflows
|
||||||
|
- Complete working examples
|
||||||
|
- Use cases and key techniques
|
||||||
|
- Pattern selection guide
|
||||||
|
|
||||||
|
### Built-in Functions
|
||||||
|
- Complete $helpers.httpRequest() reference
|
||||||
|
- DateTime/Luxon operations (formatting, parsing, arithmetic)
|
||||||
|
- $jmespath() for JSON queries
|
||||||
|
- Persistent storage with $getWorkflowStaticData()
|
||||||
|
- Standard JavaScript and Node.js modules
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critical Gotchas Highlighted
|
||||||
|
|
||||||
|
### #1: Webhook Data Structure
|
||||||
|
**MOST COMMON MISTAKE**: Webhook data is under `.body`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG
|
||||||
|
const name = $json.name;
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
const name = $json.body.name;
|
||||||
|
```
|
||||||
|
|
||||||
|
### #2: Return Format
|
||||||
|
**CRITICAL**: Must return array with json property
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG
|
||||||
|
return {json: {result: 'success'}};
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
return [{json: {result: 'success'}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### #3: Expression Syntax
|
||||||
|
**Don't use `{{}}` in Code nodes**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG
|
||||||
|
const value = "{{ $json.field }}";
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
const value = $json.field;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration with Other Skills
|
||||||
|
|
||||||
|
### n8n Expression Syntax
|
||||||
|
- **Distinction**: Expressions use `{{}}` in OTHER nodes
|
||||||
|
- **Code nodes**: Use JavaScript directly (no `{{}}`)
|
||||||
|
- **When to use each**: Code vs expressions decision guide
|
||||||
|
|
||||||
|
### n8n MCP Tools Expert
|
||||||
|
- Find Code node: `search_nodes({query: "code"})`
|
||||||
|
- Get configuration: `get_node_essentials("nodes-base.code")`
|
||||||
|
- Validate code: `validate_node_operation()`
|
||||||
|
|
||||||
|
### n8n Node Configuration
|
||||||
|
- Mode selection (All Items vs Each Item)
|
||||||
|
- Language selection (JavaScript vs Python)
|
||||||
|
- Understanding property dependencies
|
||||||
|
|
||||||
|
### n8n Workflow Patterns
|
||||||
|
- Code nodes in transformation step
|
||||||
|
- Webhook → Code → API pattern
|
||||||
|
- Error handling in workflows
|
||||||
|
|
||||||
|
### n8n Validation Expert
|
||||||
|
- Validate Code node configuration
|
||||||
|
- Handle validation errors
|
||||||
|
- Auto-fix common issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use Code Node
|
||||||
|
|
||||||
|
**Use Code node when:**
|
||||||
|
- ✅ Complex transformations requiring multiple steps
|
||||||
|
- ✅ Custom calculations or business logic
|
||||||
|
- ✅ Recursive operations
|
||||||
|
- ✅ API response parsing with complex structure
|
||||||
|
- ✅ Multi-step conditionals
|
||||||
|
- ✅ Data aggregation across items
|
||||||
|
|
||||||
|
**Consider other nodes when:**
|
||||||
|
- ❌ Simple field mapping → Use **Set** node
|
||||||
|
- ❌ Basic filtering → Use **Filter** node
|
||||||
|
- ❌ Simple conditionals → Use **IF** or **Switch** node
|
||||||
|
- ❌ HTTP requests only → Use **HTTP Request** node
|
||||||
|
|
||||||
|
**Code node excels at**: Complex logic that would require chaining many simple nodes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
**Before this skill**:
|
||||||
|
- Users confused by mode selection
|
||||||
|
- Frequent return format errors
|
||||||
|
- Expression syntax mistakes
|
||||||
|
- Webhook data access failures
|
||||||
|
- Missing null check crashes
|
||||||
|
|
||||||
|
**After this skill**:
|
||||||
|
- Clear mode selection guidance
|
||||||
|
- Understanding of return format
|
||||||
|
- JavaScript vs expression distinction
|
||||||
|
- Correct webhook data access
|
||||||
|
- Safe null-handling patterns
|
||||||
|
- Production-ready code patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Essential Rules
|
||||||
|
1. Choose "All Items" mode (recommended)
|
||||||
|
2. Access data: `$input.all()`, `$input.first()`, `$input.item`
|
||||||
|
3. **MUST return**: `[{json: {...}}]` format
|
||||||
|
4. **Webhook data**: Under `.body` property
|
||||||
|
5. **No `{{}}` syntax**: Use JavaScript directly
|
||||||
|
|
||||||
|
### Most Common Patterns
|
||||||
|
- Batch processing → $input.all() + map/filter
|
||||||
|
- Single item → $input.first()
|
||||||
|
- Aggregation → reduce()
|
||||||
|
- HTTP requests → $helpers.httpRequest()
|
||||||
|
- Date handling → DateTime (Luxon)
|
||||||
|
|
||||||
|
### Error Prevention
|
||||||
|
- Always return data
|
||||||
|
- Check for null/undefined
|
||||||
|
- Use try-catch for risky operations
|
||||||
|
- Test with empty input
|
||||||
|
- Use console.log() for debugging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- **n8n Code Node Guide**: https://docs.n8n.io/code/code-node/
|
||||||
|
- **Built-in Methods Reference**: https://docs.n8n.io/code-examples/methods-variables-reference/
|
||||||
|
- **Luxon Documentation**: https://moment.github.io/luxon/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Evaluations
|
||||||
|
|
||||||
|
**5 test scenarios** covering:
|
||||||
|
1. Webhook body gotcha (most common mistake)
|
||||||
|
2. Return format error (missing array wrapper)
|
||||||
|
3. HTTP request with $helpers.httpRequest()
|
||||||
|
4. Aggregation pattern with $input.all()
|
||||||
|
5. Expression syntax confusion (using `{{}}`)
|
||||||
|
|
||||||
|
Each evaluation tests skill activation, correct guidance, and reference to appropriate documentation files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **v1.0** (2025-01-20): Initial implementation
|
||||||
|
- SKILL.md with comprehensive overview
|
||||||
|
- DATA_ACCESS.md covering all access patterns
|
||||||
|
- COMMON_PATTERNS.md with 10 production patterns
|
||||||
|
- ERROR_PATTERNS.md covering top 5 errors
|
||||||
|
- BUILTIN_FUNCTIONS.md complete reference
|
||||||
|
- 5 evaluation scenarios
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
|
||||||
|
|
||||||
|
Part of the n8n-skills collection.
|
||||||
699
docs/skills/n8n-automation/javascript-code/SKILL.md
Normal file
699
docs/skills/n8n-automation/javascript-code/SKILL.md
Normal file
@@ -0,0 +1,699 @@
|
|||||||
|
---
|
||||||
|
name: n8n-code-javascript
|
||||||
|
description: Write JavaScript code in n8n Code nodes. Use when writing JavaScript in n8n, using $input/$json/$node syntax, making HTTP requests with $helpers, working with dates using DateTime, troubleshooting Code node errors, or choosing between Code node modes.
|
||||||
|
---
|
||||||
|
|
||||||
|
# JavaScript Code Node
|
||||||
|
|
||||||
|
Expert guidance for writing JavaScript code in n8n Code nodes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Basic template for Code nodes
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
// Process data
|
||||||
|
const processed = items.map(item => ({
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
processed: true,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Essential Rules
|
||||||
|
|
||||||
|
1. **Choose "Run Once for All Items" mode** (recommended for most use cases)
|
||||||
|
2. **Access data**: `$input.all()`, `$input.first()`, or `$input.item`
|
||||||
|
3. **CRITICAL**: Must return `[{json: {...}}]` format
|
||||||
|
4. **CRITICAL**: Webhook data is under `$json.body` (not `$json` directly)
|
||||||
|
5. **Built-ins available**: $helpers.httpRequest(), DateTime (Luxon), $jmespath()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mode Selection Guide
|
||||||
|
|
||||||
|
The Code node offers two execution modes. Choose based on your use case:
|
||||||
|
|
||||||
|
### Run Once for All Items (Recommended - Default)
|
||||||
|
|
||||||
|
**Use this mode for:** 95% of use cases
|
||||||
|
|
||||||
|
- **How it works**: Code executes **once** regardless of input count
|
||||||
|
- **Data access**: `$input.all()` or `items` array
|
||||||
|
- **Best for**: Aggregation, filtering, batch processing, transformations, API calls with all data
|
||||||
|
- **Performance**: Faster for multiple items (single execution)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Example: Calculate total from all items
|
||||||
|
const allItems = $input.all();
|
||||||
|
const total = allItems.reduce((sum, item) => sum + (item.json.amount || 0), 0);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
total,
|
||||||
|
count: allItems.length,
|
||||||
|
average: total / allItems.length
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- ✅ Comparing items across the dataset
|
||||||
|
- ✅ Calculating totals, averages, or statistics
|
||||||
|
- ✅ Sorting or ranking items
|
||||||
|
- ✅ Deduplication
|
||||||
|
- ✅ Building aggregated reports
|
||||||
|
- ✅ Combining data from multiple items
|
||||||
|
|
||||||
|
### Run Once for Each Item
|
||||||
|
|
||||||
|
**Use this mode for:** Specialized cases only
|
||||||
|
|
||||||
|
- **How it works**: Code executes **separately** for each input item
|
||||||
|
- **Data access**: `$input.item` or `$item`
|
||||||
|
- **Best for**: Item-specific logic, independent operations, per-item validation
|
||||||
|
- **Performance**: Slower for large datasets (multiple executions)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Example: Add processing timestamp to each item
|
||||||
|
const item = $input.item;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...item.json,
|
||||||
|
processed: true,
|
||||||
|
processedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- ✅ Each item needs independent API call
|
||||||
|
- ✅ Per-item validation with different error handling
|
||||||
|
- ✅ Item-specific transformations based on item properties
|
||||||
|
- ✅ When items must be processed separately for business logic
|
||||||
|
|
||||||
|
**Decision Shortcut:**
|
||||||
|
- **Need to look at multiple items?** → Use "All Items" mode
|
||||||
|
- **Each item completely independent?** → Use "Each Item" mode
|
||||||
|
- **Not sure?** → Use "All Items" mode (you can always loop inside)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Access Patterns
|
||||||
|
|
||||||
|
### Pattern 1: $input.all() - Most Common
|
||||||
|
|
||||||
|
**Use when**: Processing arrays, batch operations, aggregations
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get all items from previous node
|
||||||
|
const allItems = $input.all();
|
||||||
|
|
||||||
|
// Filter, map, reduce as needed
|
||||||
|
const valid = allItems.filter(item => item.json.status === 'active');
|
||||||
|
const mapped = valid.map(item => ({
|
||||||
|
json: {
|
||||||
|
id: item.json.id,
|
||||||
|
name: item.json.name
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: $input.first() - Very Common
|
||||||
|
|
||||||
|
**Use when**: Working with single objects, API responses, first-in-first-out
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get first item only
|
||||||
|
const firstItem = $input.first();
|
||||||
|
const data = firstItem.json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
result: processData(data),
|
||||||
|
processedAt: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: $input.item - Each Item Mode Only
|
||||||
|
|
||||||
|
**Use when**: In "Run Once for Each Item" mode
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Current item in loop (Each Item mode only)
|
||||||
|
const currentItem = $input.item;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
...currentItem.json,
|
||||||
|
itemProcessed: true
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 4: $node - Reference Other Nodes
|
||||||
|
|
||||||
|
**Use when**: Need data from specific nodes in workflow
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Get output from specific node
|
||||||
|
const webhookData = $node["Webhook"].json;
|
||||||
|
const httpData = $node["HTTP Request"].json;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
combined: {
|
||||||
|
webhook: webhookData,
|
||||||
|
api: httpData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for comprehensive guide
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critical: Webhook Data Structure
|
||||||
|
|
||||||
|
**MOST COMMON MISTAKE**: Webhook data is nested under `.body`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG - Will return undefined
|
||||||
|
const name = $json.name;
|
||||||
|
const email = $json.email;
|
||||||
|
|
||||||
|
// ✅ CORRECT - Webhook data is under .body
|
||||||
|
const name = $json.body.name;
|
||||||
|
const email = $json.body.email;
|
||||||
|
|
||||||
|
// Or with $input
|
||||||
|
const webhookData = $input.first().json.body;
|
||||||
|
const name = webhookData.name;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why**: Webhook node wraps all request data under `body` property. This includes POST data, query parameters, and JSON payloads.
|
||||||
|
|
||||||
|
**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for full webhook structure details
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Return Format Requirements
|
||||||
|
|
||||||
|
**CRITICAL RULE**: Always return array of objects with `json` property
|
||||||
|
|
||||||
|
### Correct Return Formats
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ Single result
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
field1: value1,
|
||||||
|
field2: value2
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ✅ Multiple results
|
||||||
|
return [
|
||||||
|
{json: {id: 1, data: 'first'}},
|
||||||
|
{json: {id: 2, data: 'second'}}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ✅ Transformed array
|
||||||
|
const transformed = $input.all()
|
||||||
|
.filter(item => item.json.valid)
|
||||||
|
.map(item => ({
|
||||||
|
json: {
|
||||||
|
id: item.json.id,
|
||||||
|
processed: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return transformed;
|
||||||
|
|
||||||
|
// ✅ Empty result (when no data to return)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
// ✅ Conditional return
|
||||||
|
if (shouldProcess) {
|
||||||
|
return [{json: processedData}];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Incorrect Return Formats
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Object without array wrapper
|
||||||
|
return {
|
||||||
|
json: {field: value}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ❌ WRONG: Array without json wrapper
|
||||||
|
return [{field: value}];
|
||||||
|
|
||||||
|
// ❌ WRONG: Plain string
|
||||||
|
return "processed";
|
||||||
|
|
||||||
|
// ❌ WRONG: Raw data without mapping
|
||||||
|
return $input.all(); // Missing .map()
|
||||||
|
|
||||||
|
// ❌ WRONG: Incomplete structure
|
||||||
|
return [{data: value}]; // Should be {json: value}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why it matters**: Next nodes expect array format. Incorrect format causes workflow execution to fail.
|
||||||
|
|
||||||
|
**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) #3 for detailed error solutions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Patterns Overview
|
||||||
|
|
||||||
|
Based on production workflows, here are the most useful patterns:
|
||||||
|
|
||||||
|
### 1. Multi-Source Data Aggregation
|
||||||
|
Combine data from multiple APIs, webhooks, or nodes
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const allItems = $input.all();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const item of allItems) {
|
||||||
|
const sourceName = item.json.name || 'Unknown';
|
||||||
|
// Parse source-specific structure
|
||||||
|
if (sourceName === 'API1' && item.json.data) {
|
||||||
|
results.push({
|
||||||
|
json: {
|
||||||
|
title: item.json.data.title,
|
||||||
|
source: 'API1'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Filtering with Regex
|
||||||
|
Extract patterns, mentions, or keywords from text
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const pattern = /\b([A-Z]{2,5})\b/g;
|
||||||
|
const matches = {};
|
||||||
|
|
||||||
|
for (const item of $input.all()) {
|
||||||
|
const text = item.json.text;
|
||||||
|
const found = text.match(pattern);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
found.forEach(match => {
|
||||||
|
matches[match] = (matches[match] || 0) + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{json: {matches}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Data Transformation & Enrichment
|
||||||
|
Map fields, normalize formats, add computed fields
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
return items.map(item => {
|
||||||
|
const data = item.json;
|
||||||
|
const nameParts = data.name.split(' ');
|
||||||
|
|
||||||
|
return {
|
||||||
|
json: {
|
||||||
|
first_name: nameParts[0],
|
||||||
|
last_name: nameParts.slice(1).join(' '),
|
||||||
|
email: data.email,
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Top N Filtering & Ranking
|
||||||
|
Sort and limit results
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
const topItems = items
|
||||||
|
.sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
|
||||||
|
.slice(0, 10);
|
||||||
|
|
||||||
|
return topItems.map(item => ({json: item.json}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Aggregation & Reporting
|
||||||
|
Sum, count, group data
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
const total = items.reduce((sum, item) => sum + (item.json.amount || 0), 0);
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
total,
|
||||||
|
count: items.length,
|
||||||
|
average: total / items.length,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**See**: [COMMON_PATTERNS.md](COMMON_PATTERNS.md) for 10 detailed production patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Prevention - Top 5 Mistakes
|
||||||
|
|
||||||
|
### #1: Empty Code or Missing Return (Most Common)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: No return statement
|
||||||
|
const items = $input.all();
|
||||||
|
// ... processing code ...
|
||||||
|
// Forgot to return!
|
||||||
|
|
||||||
|
// ✅ CORRECT: Always return data
|
||||||
|
const items = $input.all();
|
||||||
|
// ... processing ...
|
||||||
|
return items.map(item => ({json: item.json}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### #2: Expression Syntax Confusion
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Using n8n expression syntax in code
|
||||||
|
const value = "{{ $json.field }}";
|
||||||
|
|
||||||
|
// ✅ CORRECT: Use JavaScript template literals
|
||||||
|
const value = `${$json.field}`;
|
||||||
|
|
||||||
|
// ✅ CORRECT: Direct access
|
||||||
|
const value = $input.first().json.field;
|
||||||
|
```
|
||||||
|
|
||||||
|
### #3: Incorrect Return Wrapper
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Returning object instead of array
|
||||||
|
return {json: {result: 'success'}};
|
||||||
|
|
||||||
|
// ✅ CORRECT: Array wrapper required
|
||||||
|
return [{json: {result: 'success'}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### #4: Missing Null Checks
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Crashes if field doesn't exist
|
||||||
|
const value = item.json.user.email;
|
||||||
|
|
||||||
|
// ✅ CORRECT: Safe access with optional chaining
|
||||||
|
const value = item.json?.user?.email || 'no-email@example.com';
|
||||||
|
|
||||||
|
// ✅ CORRECT: Guard clause
|
||||||
|
if (!item.json.user) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const value = item.json.user.email;
|
||||||
|
```
|
||||||
|
|
||||||
|
### #5: Webhook Body Nesting
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ WRONG: Direct access to webhook data
|
||||||
|
const email = $json.email;
|
||||||
|
|
||||||
|
// ✅ CORRECT: Webhook data under .body
|
||||||
|
const email = $json.body.email;
|
||||||
|
```
|
||||||
|
|
||||||
|
**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) for comprehensive error guide
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Built-in Functions & Helpers
|
||||||
|
|
||||||
|
### $helpers.httpRequest()
|
||||||
|
|
||||||
|
Make HTTP requests from within code:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://api.example.com/data',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: {data: response}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### DateTime (Luxon)
|
||||||
|
|
||||||
|
Date and time operations:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Current time
|
||||||
|
const now = DateTime.now();
|
||||||
|
|
||||||
|
// Format dates
|
||||||
|
const formatted = now.toFormat('yyyy-MM-dd');
|
||||||
|
const iso = now.toISO();
|
||||||
|
|
||||||
|
// Date arithmetic
|
||||||
|
const tomorrow = now.plus({days: 1});
|
||||||
|
const lastWeek = now.minus({weeks: 1});
|
||||||
|
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
today: formatted,
|
||||||
|
tomorrow: tomorrow.toFormat('yyyy-MM-dd')
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
### $jmespath()
|
||||||
|
|
||||||
|
Query JSON structures:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const data = $input.first().json;
|
||||||
|
|
||||||
|
// Filter array
|
||||||
|
const adults = $jmespath(data, 'users[?age >= `18`]');
|
||||||
|
|
||||||
|
// Extract fields
|
||||||
|
const names = $jmespath(data, 'users[*].name');
|
||||||
|
|
||||||
|
return [{json: {adults, names}}];
|
||||||
|
```
|
||||||
|
|
||||||
|
**See**: [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) for complete reference
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Always Validate Input Data
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const items = $input.all();
|
||||||
|
|
||||||
|
// Check if data exists
|
||||||
|
if (!items || items.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate structure
|
||||||
|
if (!items[0].json) {
|
||||||
|
return [{json: {error: 'Invalid input format'}}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue processing...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Use Try-Catch for Error Handling
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
try {
|
||||||
|
const response = await $helpers.httpRequest({
|
||||||
|
url: 'https://api.example.com/data'
|
||||||
|
});
|
||||||
|
|
||||||
|
return [{json: {success: true, data: response}}];
|
||||||
|
} catch (error) {
|
||||||
|
return [{
|
||||||
|
json: {
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Prefer Array Methods Over Loops
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ GOOD: Functional approach
|
||||||
|
const processed = $input.all()
|
||||||
|
.filter(item => item.json.valid)
|
||||||
|
.map(item => ({json: {id: item.json.id}}));
|
||||||
|
|
||||||
|
// ❌ SLOWER: Manual loop
|
||||||
|
const processed = [];
|
||||||
|
for (const item of $input.all()) {
|
||||||
|
if (item.json.valid) {
|
||||||
|
processed.push({json: {id: item.json.id}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Filter Early, Process Late
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ GOOD: Filter first to reduce processing
|
||||||
|
const processed = $input.all()
|
||||||
|
.filter(item => item.json.status === 'active') // Reduce dataset first
|
||||||
|
.map(item => expensiveTransformation(item)); // Then transform
|
||||||
|
|
||||||
|
// ❌ WASTEFUL: Transform everything, then filter
|
||||||
|
const processed = $input.all()
|
||||||
|
.map(item => expensiveTransformation(item)) // Wastes CPU
|
||||||
|
.filter(item => item.json.status === 'active');
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Use Descriptive Variable Names
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ GOOD: Clear intent
|
||||||
|
const activeUsers = $input.all().filter(item => item.json.active);
|
||||||
|
const totalRevenue = activeUsers.reduce((sum, user) => sum + user.json.revenue, 0);
|
||||||
|
|
||||||
|
// ❌ BAD: Unclear purpose
|
||||||
|
const a = $input.all().filter(item => item.json.active);
|
||||||
|
const t = a.reduce((s, u) => s + u.json.revenue, 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Debug with console.log()
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Debug statements appear in browser console
|
||||||
|
const items = $input.all();
|
||||||
|
console.log(`Processing ${items.length} items`);
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
console.log('Item data:', item.json);
|
||||||
|
// Process...
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use Code Node
|
||||||
|
|
||||||
|
Use Code node when:
|
||||||
|
- ✅ Complex transformations requiring multiple steps
|
||||||
|
- ✅ Custom calculations or business logic
|
||||||
|
- ✅ Recursive operations
|
||||||
|
- ✅ API response parsing with complex structure
|
||||||
|
- ✅ Multi-step conditionals
|
||||||
|
- ✅ Data aggregation across items
|
||||||
|
|
||||||
|
Consider other nodes when:
|
||||||
|
- ❌ Simple field mapping → Use **Set** node
|
||||||
|
- ❌ Basic filtering → Use **Filter** node
|
||||||
|
- ❌ Simple conditionals → Use **IF** or **Switch** node
|
||||||
|
- ❌ HTTP requests only → Use **HTTP Request** node
|
||||||
|
|
||||||
|
**Code node excels at**: Complex logic that would require chaining many simple nodes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration with Other Skills
|
||||||
|
|
||||||
|
### Works With:
|
||||||
|
|
||||||
|
**n8n Expression Syntax**:
|
||||||
|
- Expressions use `{{ }}` syntax in other nodes
|
||||||
|
- Code nodes use JavaScript directly (no `{{ }}`)
|
||||||
|
- When to use expressions vs code
|
||||||
|
|
||||||
|
**n8n MCP Tools Expert**:
|
||||||
|
- How to find Code node: `search_nodes({query: "code"})`
|
||||||
|
- Get configuration help: `get_node_essentials("nodes-base.code")`
|
||||||
|
- Validate code: `validate_node_operation()`
|
||||||
|
|
||||||
|
**n8n Node Configuration**:
|
||||||
|
- Mode selection (All Items vs Each Item)
|
||||||
|
- Language selection (JavaScript vs Python)
|
||||||
|
- Understanding property dependencies
|
||||||
|
|
||||||
|
**n8n Workflow Patterns**:
|
||||||
|
- Code nodes in transformation step
|
||||||
|
- Webhook → Code → API pattern
|
||||||
|
- Error handling in workflows
|
||||||
|
|
||||||
|
**n8n Validation Expert**:
|
||||||
|
- Validate Code node configuration
|
||||||
|
- Handle validation errors
|
||||||
|
- Auto-fix common issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference Checklist
|
||||||
|
|
||||||
|
Before deploying Code nodes, verify:
|
||||||
|
|
||||||
|
- [ ] **Code is not empty** - Must have meaningful logic
|
||||||
|
- [ ] **Return statement exists** - Must return array of objects
|
||||||
|
- [ ] **Proper return format** - Each item: `{json: {...}}`
|
||||||
|
- [ ] **Data access correct** - Using `$input.all()`, `$input.first()`, or `$input.item`
|
||||||
|
- [ ] **No n8n expressions** - Use JavaScript template literals: `` `${value}` ``
|
||||||
|
- [ ] **Error handling** - Guard clauses for null/undefined inputs
|
||||||
|
- [ ] **Webhook data** - Access via `.body` if from webhook
|
||||||
|
- [ ] **Mode selection** - "All Items" for most cases
|
||||||
|
- [ ] **Performance** - Prefer map/filter over manual loops
|
||||||
|
- [ ] **Output consistent** - All code paths return same structure
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
### Related Files
|
||||||
|
- [DATA_ACCESS.md](DATA_ACCESS.md) - Comprehensive data access patterns
|
||||||
|
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - 10 production-tested patterns
|
||||||
|
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Top 5 errors and solutions
|
||||||
|
- [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) - Complete built-in reference
|
||||||
|
|
||||||
|
### n8n Documentation
|
||||||
|
- Code Node Guide: https://docs.n8n.io/code/code-node/
|
||||||
|
- Built-in Methods: https://docs.n8n.io/code-examples/methods-variables-reference/
|
||||||
|
- Luxon Documentation: https://moment.github.io/luxon/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready to write JavaScript in n8n Code nodes!** Start with simple transformations, use the error patterns guide to avoid common mistakes, and reference the pattern library for production-ready examples.
|
||||||
800
docs/skills/n8n-automation/node-configuration/DEPENDENCIES.md
Normal file
800
docs/skills/n8n-automation/node-configuration/DEPENDENCIES.md
Normal file
@@ -0,0 +1,800 @@
|
|||||||
|
# Property Dependencies Guide
|
||||||
|
|
||||||
|
Deep dive into n8n property dependencies and displayOptions mechanism.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Are Property Dependencies?
|
||||||
|
|
||||||
|
**Definition**: Rules that control when fields are visible or required based on other field values.
|
||||||
|
|
||||||
|
**Mechanism**: `displayOptions` in node schema
|
||||||
|
|
||||||
|
**Purpose**:
|
||||||
|
- Show relevant fields only
|
||||||
|
- Hide irrelevant fields
|
||||||
|
- Simplify configuration UX
|
||||||
|
- Prevent invalid configurations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## displayOptions Structure
|
||||||
|
|
||||||
|
### Basic Format
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "fieldName",
|
||||||
|
"type": "string",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"otherField": ["value1", "value2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Translation**: Show `fieldName` when `otherField` equals "value1" OR "value2"
|
||||||
|
|
||||||
|
### Show vs Hide
|
||||||
|
|
||||||
|
#### show (Most Common)
|
||||||
|
|
||||||
|
**Show field when condition matches**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendBody": [true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Meaning**: Show `body` when `sendBody = true`
|
||||||
|
|
||||||
|
#### hide (Less Common)
|
||||||
|
|
||||||
|
**Hide field when condition matches**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "advanced",
|
||||||
|
"displayOptions": {
|
||||||
|
"hide": {
|
||||||
|
"simpleMode": [true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Meaning**: Hide `advanced` when `simpleMode = true`
|
||||||
|
|
||||||
|
### Multiple Conditions (AND Logic)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendBody": [true],
|
||||||
|
"method": ["POST", "PUT", "PATCH"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Meaning**: Show `body` when:
|
||||||
|
- `sendBody = true` AND
|
||||||
|
- `method IN (POST, PUT, PATCH)`
|
||||||
|
|
||||||
|
**All conditions must match** (AND logic)
|
||||||
|
|
||||||
|
### Multiple Values (OR Logic)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "someField",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"method": ["POST", "PUT", "PATCH"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Meaning**: Show `someField` when:
|
||||||
|
- `method = POST` OR
|
||||||
|
- `method = PUT` OR
|
||||||
|
- `method = PATCH`
|
||||||
|
|
||||||
|
**Any value matches** (OR logic)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Dependency Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Boolean Toggle
|
||||||
|
|
||||||
|
**Use case**: Optional feature flag
|
||||||
|
|
||||||
|
**Example**: HTTP Request sendBody
|
||||||
|
```javascript
|
||||||
|
// Field: sendBody (boolean)
|
||||||
|
{
|
||||||
|
"name": "sendBody",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field: body (depends on sendBody)
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendBody": [true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flow**:
|
||||||
|
1. User sees sendBody checkbox
|
||||||
|
2. When checked → body field appears
|
||||||
|
3. When unchecked → body field hides
|
||||||
|
|
||||||
|
### Pattern 2: Resource/Operation Cascade
|
||||||
|
|
||||||
|
**Use case**: Different operations show different fields
|
||||||
|
|
||||||
|
**Example**: Slack message operations
|
||||||
|
```javascript
|
||||||
|
// Operation: post
|
||||||
|
{
|
||||||
|
"name": "channel",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"resource": ["message"],
|
||||||
|
"operation": ["post"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation: update
|
||||||
|
{
|
||||||
|
"name": "messageId",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"resource": ["message"],
|
||||||
|
"operation": ["update"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flow**:
|
||||||
|
1. User selects resource="message"
|
||||||
|
2. User selects operation="post" → sees channel
|
||||||
|
3. User changes to operation="update" → channel hides, messageId shows
|
||||||
|
|
||||||
|
### Pattern 3: Type-Specific Configuration
|
||||||
|
|
||||||
|
**Use case**: Different types need different fields
|
||||||
|
|
||||||
|
**Example**: IF node conditions
|
||||||
|
```javascript
|
||||||
|
// String operations
|
||||||
|
{
|
||||||
|
"name": "value2",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"conditions.string.0.operation": ["equals", "notEquals", "contains"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary operations (isEmpty) don't show value2
|
||||||
|
{
|
||||||
|
"displayOptions": {
|
||||||
|
"hide": {
|
||||||
|
"conditions.string.0.operation": ["isEmpty", "isNotEmpty"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 4: Method-Specific Fields
|
||||||
|
|
||||||
|
**Use case**: HTTP methods have different options
|
||||||
|
|
||||||
|
**Example**: HTTP Request
|
||||||
|
```javascript
|
||||||
|
// Query parameters (all methods can have)
|
||||||
|
{
|
||||||
|
"name": "queryParameters",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendQuery": [true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body (only certain methods)
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendBody": [true],
|
||||||
|
"method": ["POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using get_property_dependencies
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const deps = get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"nodeType": "n8n-nodes-base.httpRequest",
|
||||||
|
"dependencies": {
|
||||||
|
"body": {
|
||||||
|
"shows_when": {
|
||||||
|
"sendBody": [true],
|
||||||
|
"method": ["POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
},
|
||||||
|
"required_when_shown": true
|
||||||
|
},
|
||||||
|
"queryParameters": {
|
||||||
|
"shows_when": {
|
||||||
|
"sendQuery": [true]
|
||||||
|
},
|
||||||
|
"required_when_shown": false
|
||||||
|
},
|
||||||
|
"headerParameters": {
|
||||||
|
"shows_when": {
|
||||||
|
"sendHeaders": [true]
|
||||||
|
},
|
||||||
|
"required_when_shown": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
|
||||||
|
**✅ Use when**:
|
||||||
|
- Validation fails with "missing field" but you don't see that field
|
||||||
|
- A field appears/disappears unexpectedly
|
||||||
|
- You need to understand what controls field visibility
|
||||||
|
- Building dynamic configuration tools
|
||||||
|
|
||||||
|
**❌ Don't use when**:
|
||||||
|
- Simple configuration (use get_node_essentials)
|
||||||
|
- Just starting configuration
|
||||||
|
- Field requirements are obvious
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Complex Dependency Examples
|
||||||
|
|
||||||
|
### Example 1: HTTP Request Complete Flow
|
||||||
|
|
||||||
|
**Scenario**: Configuring POST with JSON body
|
||||||
|
|
||||||
|
**Step 1**: Set method
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
// → sendBody becomes visible
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2**: Enable body
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true
|
||||||
|
// → body field becomes visible AND required
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3**: Configure body
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json"
|
||||||
|
// → content field becomes visible AND required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4**: Add content
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "John",
|
||||||
|
"email": "john@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ✅ Valid!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependency chain**:
|
||||||
|
```
|
||||||
|
method=POST
|
||||||
|
→ sendBody visible
|
||||||
|
→ sendBody=true
|
||||||
|
→ body visible + required
|
||||||
|
→ body.contentType=json
|
||||||
|
→ body.content visible + required
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: IF Node Operator Dependencies
|
||||||
|
|
||||||
|
**Scenario**: String comparison with different operators
|
||||||
|
|
||||||
|
**Binary operator** (equals):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"operation": "equals"
|
||||||
|
// → value1 required
|
||||||
|
// → value2 required
|
||||||
|
// → singleValue should NOT be set
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Unary operator** (isEmpty):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"operation": "isEmpty"
|
||||||
|
// → value1 required
|
||||||
|
// → value2 should NOT be set
|
||||||
|
// → singleValue should be true (auto-added)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependency table**:
|
||||||
|
|
||||||
|
| Operator | value1 | value2 | singleValue |
|
||||||
|
|---|---|---|---|
|
||||||
|
| equals | Required | Required | false |
|
||||||
|
| notEquals | Required | Required | false |
|
||||||
|
| contains | Required | Required | false |
|
||||||
|
| isEmpty | Required | Hidden | true |
|
||||||
|
| isNotEmpty | Required | Hidden | true |
|
||||||
|
|
||||||
|
### Example 3: Slack Operation Matrix
|
||||||
|
|
||||||
|
**Scenario**: Different Slack operations show different fields
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// post message
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post"
|
||||||
|
// Shows: channel (required), text (required), attachments, blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// update message
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update"
|
||||||
|
// Shows: messageId (required), text (required), channel (optional)
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete message
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "delete"
|
||||||
|
// Shows: messageId (required), channel (required)
|
||||||
|
// Hides: text, attachments, blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// get message
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "get"
|
||||||
|
// Shows: messageId (required), channel (required)
|
||||||
|
// Hides: text, attachments, blocks
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Field visibility matrix**:
|
||||||
|
|
||||||
|
| Field | post | update | delete | get |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| channel | Required | Optional | Required | Required |
|
||||||
|
| text | Required | Required | Hidden | Hidden |
|
||||||
|
| messageId | Hidden | Required | Required | Required |
|
||||||
|
| attachments | Optional | Optional | Hidden | Hidden |
|
||||||
|
| blocks | Optional | Optional | Hidden | Hidden |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nested Dependencies
|
||||||
|
|
||||||
|
### What Are They?
|
||||||
|
|
||||||
|
**Definition**: Dependencies within object properties
|
||||||
|
|
||||||
|
**Example**: HTTP Request body.contentType controls body.content structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
// → content expects JSON object
|
||||||
|
"content": {
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"contentType": "form-data",
|
||||||
|
// → content expects form fields array
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"name": "field1",
|
||||||
|
"value": "value1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to Handle
|
||||||
|
|
||||||
|
**Strategy**: Configure parent first, then children
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Step 1: Parent
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"contentType": "json" // Set parent first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Children (structure determined by parent)
|
||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": { // JSON object format
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auto-Sanitization and Dependencies
|
||||||
|
|
||||||
|
### What Auto-Sanitization Fixes
|
||||||
|
|
||||||
|
**Operator structure issues** (IF/Switch nodes):
|
||||||
|
|
||||||
|
**Example**: singleValue property
|
||||||
|
```javascript
|
||||||
|
// You configure (missing singleValue)
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "isEmpty"
|
||||||
|
// Missing singleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-sanitization adds it
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "isEmpty",
|
||||||
|
"singleValue": true // ✅ Added automatically
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### What It Doesn't Fix
|
||||||
|
|
||||||
|
**Missing required fields**:
|
||||||
|
```javascript
|
||||||
|
// You configure (missing channel)
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"text": "Hello"
|
||||||
|
// Missing required field: channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-sanitization does NOT add
|
||||||
|
// You must add it yourself
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general", // ← You must add
|
||||||
|
"text": "Hello"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting Dependencies
|
||||||
|
|
||||||
|
### Problem 1: "Field X is required but not visible"
|
||||||
|
|
||||||
|
**Error**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "missing_required",
|
||||||
|
"property": "body",
|
||||||
|
"message": "body is required"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**But you don't see body field in configuration!**
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```javascript
|
||||||
|
// Check dependencies
|
||||||
|
const deps = get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find that body shows when sendBody=true
|
||||||
|
// Add sendBody
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true, // ← Now body appears!
|
||||||
|
"body": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem 2: "Field disappears when I change operation"
|
||||||
|
|
||||||
|
**Scenario**:
|
||||||
|
```javascript
|
||||||
|
// Working configuration
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general",
|
||||||
|
"text": "Hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change operation
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update", // Changed
|
||||||
|
"channel": "#general", // Still here
|
||||||
|
"text": "Updated" // Still here
|
||||||
|
// Missing: messageId (required for update!)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation error**: "messageId is required"
|
||||||
|
|
||||||
|
**Why**: Different operation = different required fields
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```javascript
|
||||||
|
// Check essentials for new operation
|
||||||
|
get_node_essentials({
|
||||||
|
nodeType: "nodes-base.slack"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure for update operation
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update",
|
||||||
|
"messageId": "1234567890", // Required for update
|
||||||
|
"text": "Updated",
|
||||||
|
"channel": "#general" // Optional for update
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem 3: "Validation passes but field doesn't save"
|
||||||
|
|
||||||
|
**Scenario**: Field hidden by dependencies after validation
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```javascript
|
||||||
|
// Configure
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"sendBody": true, // ❌ GET doesn't support body
|
||||||
|
"body": {...} // This will be stripped
|
||||||
|
}
|
||||||
|
|
||||||
|
// After save
|
||||||
|
{
|
||||||
|
"method": "GET"
|
||||||
|
// body removed because method=GET hides it
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution**: Respect dependencies from the start
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Correct approach
|
||||||
|
get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
|
||||||
|
// See that body only shows for POST/PUT/PATCH/DELETE
|
||||||
|
// Use correct method
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Conditional Required with Fallback
|
||||||
|
|
||||||
|
**Example**: Channel can be string OR expression
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Option 1: String
|
||||||
|
{
|
||||||
|
"channel": "#general"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option 2: Expression
|
||||||
|
{
|
||||||
|
"channel": "={{$json.channelName}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation accepts both
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Mutually Exclusive Fields
|
||||||
|
|
||||||
|
**Example**: Use either ID or name, not both
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use messageId
|
||||||
|
{
|
||||||
|
"messageId": "1234567890"
|
||||||
|
// name not needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// OR use messageName
|
||||||
|
{
|
||||||
|
"messageName": "thread-name"
|
||||||
|
// messageId not needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependencies ensure only one is required
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: Progressive Complexity
|
||||||
|
|
||||||
|
**Example**: Simple mode vs advanced mode
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Simple mode
|
||||||
|
{
|
||||||
|
"mode": "simple",
|
||||||
|
"text": "{{$json.message}}"
|
||||||
|
// Advanced fields hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advanced mode
|
||||||
|
{
|
||||||
|
"mode": "advanced",
|
||||||
|
"attachments": [...],
|
||||||
|
"blocks": [...],
|
||||||
|
"metadata": {...}
|
||||||
|
// Simple field hidden, advanced fields shown
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### ✅ Do
|
||||||
|
|
||||||
|
1. **Check dependencies when stuck**
|
||||||
|
```javascript
|
||||||
|
get_property_dependencies({nodeType: "..."});
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure parent properties first**
|
||||||
|
```javascript
|
||||||
|
// First: method, resource, operation
|
||||||
|
// Then: dependent fields
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Validate after changing operation**
|
||||||
|
```javascript
|
||||||
|
// Operation changed → requirements changed
|
||||||
|
validate_node_operation({...});
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Read validation errors for dependency hints**
|
||||||
|
```
|
||||||
|
Error: "body required when sendBody=true"
|
||||||
|
→ Hint: Set sendBody=true to enable body
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ Don't
|
||||||
|
|
||||||
|
1. **Don't ignore dependency errors**
|
||||||
|
```javascript
|
||||||
|
// Error: "body not visible" → Check displayOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Don't hardcode all possible fields**
|
||||||
|
```javascript
|
||||||
|
// Bad: Adding fields that will be hidden
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Don't assume operations are identical**
|
||||||
|
```javascript
|
||||||
|
// Each operation has unique requirements
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Key Concepts**:
|
||||||
|
- `displayOptions` control field visibility
|
||||||
|
- `show` = field appears when conditions match
|
||||||
|
- `hide` = field disappears when conditions match
|
||||||
|
- Multiple conditions = AND logic
|
||||||
|
- Multiple values = OR logic
|
||||||
|
|
||||||
|
**Common Patterns**:
|
||||||
|
1. Boolean toggle (sendBody → body)
|
||||||
|
2. Resource/operation cascade (different operations → different fields)
|
||||||
|
3. Type-specific config (string vs boolean conditions)
|
||||||
|
4. Method-specific fields (GET vs POST)
|
||||||
|
|
||||||
|
**Troubleshooting**:
|
||||||
|
- Field required but not visible → Check dependencies
|
||||||
|
- Field disappears after change → Operation changed requirements
|
||||||
|
- Field doesn't save → Hidden by dependencies
|
||||||
|
|
||||||
|
**Tools**:
|
||||||
|
- `get_property_dependencies` - See dependency rules
|
||||||
|
- `get_node_essentials` - See operation requirements
|
||||||
|
- Validation errors - Hints about dependencies
|
||||||
|
|
||||||
|
**Related Files**:
|
||||||
|
- **[SKILL.md](SKILL.md)** - Main configuration guide
|
||||||
|
- **[OPERATION_PATTERNS.md](OPERATION_PATTERNS.md)** - Common patterns by node type
|
||||||
@@ -0,0 +1,913 @@
|
|||||||
|
# Operation Patterns Guide
|
||||||
|
|
||||||
|
Common node configuration patterns organized by node type and operation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Purpose**: Quick reference for common node configurations
|
||||||
|
|
||||||
|
**Coverage**: Top 20 most-used nodes from 525 available
|
||||||
|
|
||||||
|
**Pattern format**:
|
||||||
|
- Minimal valid configuration
|
||||||
|
- Common options
|
||||||
|
- Real-world examples
|
||||||
|
- Gotchas and tips
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HTTP & API Nodes
|
||||||
|
|
||||||
|
### HTTP Request (nodes-base.httpRequest)
|
||||||
|
|
||||||
|
Most versatile node for HTTP operations
|
||||||
|
|
||||||
|
#### GET Request
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With query parameters**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"value": "100"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "offset",
|
||||||
|
"value": "={{$json.offset}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With authentication**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "predefinedCredentialType",
|
||||||
|
"nodeCredentialType": "httpHeaderAuth"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST with JSON
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With expressions**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "={{$json.name}}",
|
||||||
|
"email": "={{$json.email}}",
|
||||||
|
"metadata": {
|
||||||
|
"source": "n8n",
|
||||||
|
"timestamp": "={{$now.toISO()}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Remember `sendBody: true` for POST/PUT/PATCH!
|
||||||
|
|
||||||
|
#### PUT/PATCH Request
|
||||||
|
|
||||||
|
**Pattern**: Same as POST, but method changes
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "PUT", // or "PATCH"
|
||||||
|
"url": "https://api.example.com/users/123",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "Updated Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### DELETE Request
|
||||||
|
|
||||||
|
**Minimal** (no body):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"url": "https://api.example.com/users/123",
|
||||||
|
"authentication": "none"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With body** (some APIs allow):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"ids": ["123", "456"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Webhook (nodes-base.webhook)
|
||||||
|
|
||||||
|
Most common trigger - 813 searches!
|
||||||
|
|
||||||
|
#### Basic Webhook
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"path": "my-webhook",
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"responseMode": "onReceived"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Webhook data is under `$json.body`, not `$json`!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ Wrong
|
||||||
|
{
|
||||||
|
"text": "={{$json.email}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Correct
|
||||||
|
{
|
||||||
|
"text": "={{$json.body.email}}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Webhook with Authentication
|
||||||
|
|
||||||
|
**Header auth**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"path": "secure-webhook",
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"responseMode": "onReceived",
|
||||||
|
"authentication": "headerAuth",
|
||||||
|
"options": {
|
||||||
|
"responseCode": 200,
|
||||||
|
"responseData": "{\n \"success\": true\n}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Webhook Returning Data
|
||||||
|
|
||||||
|
**Custom response**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"path": "my-webhook",
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"responseMode": "lastNode", // Return data from last node
|
||||||
|
"options": {
|
||||||
|
"responseCode": 201,
|
||||||
|
"responseHeaders": {
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communication Nodes
|
||||||
|
|
||||||
|
### Slack (nodes-base.slack)
|
||||||
|
|
||||||
|
Popular choice for AI agent workflows
|
||||||
|
|
||||||
|
#### Post Message
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general",
|
||||||
|
"text": "Hello from n8n!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With dynamic content**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "={{$json.channel}}",
|
||||||
|
"text": "New user: {{$json.name}} ({{$json.email}})"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With attachments**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#alerts",
|
||||||
|
"text": "Error Alert",
|
||||||
|
"attachments": [
|
||||||
|
{
|
||||||
|
"color": "#ff0000",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"title": "Error Type",
|
||||||
|
"value": "={{$json.errorType}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Timestamp",
|
||||||
|
"value": "={{$now.toLocaleString()}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Channel must start with `#` for public channels or be a channel ID!
|
||||||
|
|
||||||
|
#### Update Message
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update",
|
||||||
|
"messageId": "1234567890.123456", // From previous message post
|
||||||
|
"text": "Updated message content"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: `messageId` required, `channel` optional (can be inferred)
|
||||||
|
|
||||||
|
#### Create Channel
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "channel",
|
||||||
|
"operation": "create",
|
||||||
|
"name": "new-project-channel", // Lowercase, no spaces
|
||||||
|
"isPrivate": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Channel name must be lowercase, no spaces, 1-80 chars!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gmail (nodes-base.gmail)
|
||||||
|
|
||||||
|
Popular for email automation
|
||||||
|
|
||||||
|
#### Send Email
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "send",
|
||||||
|
"to": "user@example.com",
|
||||||
|
"subject": "Hello from n8n",
|
||||||
|
"message": "This is the email body"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With dynamic content**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "send",
|
||||||
|
"to": "={{$json.email}}",
|
||||||
|
"subject": "Order Confirmation #{{$json.orderId}}",
|
||||||
|
"message": "Dear {{$json.name}},\n\nYour order has been confirmed.\n\nThank you!",
|
||||||
|
"options": {
|
||||||
|
"ccList": "admin@example.com",
|
||||||
|
"replyTo": "support@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Email
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "getAll",
|
||||||
|
"returnAll": false,
|
||||||
|
"limit": 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With filters**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "getAll",
|
||||||
|
"returnAll": false,
|
||||||
|
"limit": 50,
|
||||||
|
"filters": {
|
||||||
|
"q": "is:unread from:important@example.com",
|
||||||
|
"labelIds": ["INBOX"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database Nodes
|
||||||
|
|
||||||
|
### Postgres (nodes-base.postgres)
|
||||||
|
|
||||||
|
Database operations - 456 templates
|
||||||
|
|
||||||
|
#### Execute Query
|
||||||
|
|
||||||
|
**Minimal** (SELECT):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "executeQuery",
|
||||||
|
"query": "SELECT * FROM users WHERE active = true LIMIT 100"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With parameters** (SQL injection prevention):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "executeQuery",
|
||||||
|
"query": "SELECT * FROM users WHERE email = $1 AND active = $2",
|
||||||
|
"additionalFields": {
|
||||||
|
"mode": "list",
|
||||||
|
"queryParameters": "user@example.com,true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: ALWAYS use parameterized queries for user input!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ BAD - SQL injection risk!
|
||||||
|
{
|
||||||
|
"query": "SELECT * FROM users WHERE email = '{{$json.email}}'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ GOOD - Parameterized
|
||||||
|
{
|
||||||
|
"query": "SELECT * FROM users WHERE email = $1",
|
||||||
|
"additionalFields": {
|
||||||
|
"mode": "list",
|
||||||
|
"queryParameters": "={{$json.email}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Insert
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "insert",
|
||||||
|
"table": "users",
|
||||||
|
"columns": "name,email,created_at",
|
||||||
|
"additionalFields": {
|
||||||
|
"mode": "list",
|
||||||
|
"queryParameters": "John Doe,john@example.com,NOW()"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With expressions**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "insert",
|
||||||
|
"table": "users",
|
||||||
|
"columns": "name,email,metadata",
|
||||||
|
"additionalFields": {
|
||||||
|
"mode": "list",
|
||||||
|
"queryParameters": "={{$json.name}},={{$json.email}},{{JSON.stringify($json)}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Update
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "update",
|
||||||
|
"table": "users",
|
||||||
|
"updateKey": "id",
|
||||||
|
"columns": "name,email",
|
||||||
|
"additionalFields": {
|
||||||
|
"mode": "list",
|
||||||
|
"queryParameters": "={{$json.id}},Updated Name,newemail@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Transformation Nodes
|
||||||
|
|
||||||
|
### Set (nodes-base.set)
|
||||||
|
|
||||||
|
Most used transformation - 68% of workflows!
|
||||||
|
|
||||||
|
#### Set Fixed Values
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "manual",
|
||||||
|
"duplicateItem": false,
|
||||||
|
"assignments": {
|
||||||
|
"assignments": [
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"value": "active",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"value": 100,
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Set from Input Data
|
||||||
|
|
||||||
|
**Mapping data**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "manual",
|
||||||
|
"duplicateItem": false,
|
||||||
|
"assignments": {
|
||||||
|
"assignments": [
|
||||||
|
{
|
||||||
|
"name": "fullName",
|
||||||
|
"value": "={{$json.firstName}} {{$json.lastName}}",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "email",
|
||||||
|
"value": "={{$json.email.toLowerCase()}}",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "timestamp",
|
||||||
|
"value": "={{$now.toISO()}}",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Use correct `type` for each field!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ Wrong type
|
||||||
|
{
|
||||||
|
"name": "age",
|
||||||
|
"value": "25", // String
|
||||||
|
"type": "string" // Will be string "25"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Correct type
|
||||||
|
{
|
||||||
|
"name": "age",
|
||||||
|
"value": 25, // Number
|
||||||
|
"type": "number" // Will be number 25
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Code (nodes-base.code)
|
||||||
|
|
||||||
|
JavaScript execution - 42% of workflows
|
||||||
|
|
||||||
|
#### Simple Transformation
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "runOnceForAllItems",
|
||||||
|
"jsCode": "return $input.all().map(item => ({\n json: {\n name: item.json.name.toUpperCase(),\n email: item.json.email\n }\n}));"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Per-item processing**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Process each item\nconst data = $input.item.json;\n\nreturn {\n json: {\n fullName: `${data.firstName} ${data.lastName}`,\n email: data.email.toLowerCase(),\n timestamp: new Date().toISOString()\n }\n};"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: In Code nodes, use `$input.item.json` or `$input.all()`, NOT `{{...}}`!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ Wrong - expressions don't work in Code nodes
|
||||||
|
{
|
||||||
|
"jsCode": "const name = '={{$json.name}}';"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Correct - direct access
|
||||||
|
{
|
||||||
|
"jsCode": "const name = $input.item.json.name;"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conditional Nodes
|
||||||
|
|
||||||
|
### IF (nodes-base.if)
|
||||||
|
|
||||||
|
Conditional logic - 38% of workflows
|
||||||
|
|
||||||
|
#### String Comparison
|
||||||
|
|
||||||
|
**Equals** (binary):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "active"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Contains** (binary):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.email}}",
|
||||||
|
"operation": "contains",
|
||||||
|
"value2": "@example.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**isEmpty** (unary):
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.email}}",
|
||||||
|
"operation": "isEmpty"
|
||||||
|
// No value2 - unary operator
|
||||||
|
// singleValue: true added by auto-sanitization
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Unary operators (isEmpty, isNotEmpty) don't need value2!
|
||||||
|
|
||||||
|
#### Number Comparison
|
||||||
|
|
||||||
|
**Greater than**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.age}}",
|
||||||
|
"operation": "larger",
|
||||||
|
"value2": 18
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Boolean Comparison
|
||||||
|
|
||||||
|
**Is true**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"boolean": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.isActive}}",
|
||||||
|
"operation": "true"
|
||||||
|
// Unary - no value2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multiple Conditions (AND)
|
||||||
|
|
||||||
|
**All must match**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "active"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"number": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.age}}",
|
||||||
|
"operation": "larger",
|
||||||
|
"value2": 18
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"combineOperation": "all" // AND logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multiple Conditions (OR)
|
||||||
|
|
||||||
|
**Any can match**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "active"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "pending"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"combineOperation": "any" // OR logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Switch (nodes-base.switch)
|
||||||
|
|
||||||
|
Multi-way routing - 18% of workflows
|
||||||
|
|
||||||
|
#### Basic Switch
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "rules",
|
||||||
|
"rules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "active"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "pending"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fallbackOutput": "extra" // Catch-all for non-matching
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Number of rules must match number of outputs!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AI Nodes
|
||||||
|
|
||||||
|
### OpenAI (nodes-langchain.openAi)
|
||||||
|
|
||||||
|
AI operations - 234 templates
|
||||||
|
|
||||||
|
#### Chat Completion
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "chat",
|
||||||
|
"operation": "complete",
|
||||||
|
"messages": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "={{$json.prompt}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**With system prompt**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "chat",
|
||||||
|
"operation": "complete",
|
||||||
|
"messages": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful assistant specialized in customer support."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "={{$json.userMessage}}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0.7,
|
||||||
|
"maxTokens": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schedule Nodes
|
||||||
|
|
||||||
|
### Schedule Trigger (nodes-base.scheduleTrigger)
|
||||||
|
|
||||||
|
Time-based workflows - 28% have schedule triggers
|
||||||
|
|
||||||
|
#### Daily at Specific Time
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"rule": {
|
||||||
|
"interval": [
|
||||||
|
{
|
||||||
|
"field": "hours",
|
||||||
|
"hoursInterval": 24
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hour": 9,
|
||||||
|
"minute": 0,
|
||||||
|
"timezone": "America/New_York"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gotcha**: Always set timezone explicitly!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ❌ Bad - uses server timezone
|
||||||
|
{
|
||||||
|
"rule": {
|
||||||
|
"interval": [...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Good - explicit timezone
|
||||||
|
{
|
||||||
|
"rule": {
|
||||||
|
"interval": [...],
|
||||||
|
"timezone": "America/New_York"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Every N Minutes
|
||||||
|
|
||||||
|
**Minimal**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"rule": {
|
||||||
|
"interval": [
|
||||||
|
{
|
||||||
|
"field": "minutes",
|
||||||
|
"minutesInterval": 15
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cron Expression
|
||||||
|
|
||||||
|
**Advanced scheduling**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"mode": "cron",
|
||||||
|
"cronExpression": "0 */2 * * *", // Every 2 hours
|
||||||
|
"timezone": "America/New_York"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Key Patterns by Category**:
|
||||||
|
|
||||||
|
| Category | Most Common | Key Gotcha |
|
||||||
|
|---|---|---|
|
||||||
|
| HTTP/API | GET, POST JSON | Remember sendBody: true |
|
||||||
|
| Webhooks | POST receiver | Data under $json.body |
|
||||||
|
| Communication | Slack post | Channel format (#name) |
|
||||||
|
| Database | SELECT with params | Use parameterized queries |
|
||||||
|
| Transform | Set assignments | Correct type per field |
|
||||||
|
| Conditional | IF string equals | Unary vs binary operators |
|
||||||
|
| AI | OpenAI chat | System + user messages |
|
||||||
|
| Schedule | Daily at time | Set timezone explicitly |
|
||||||
|
|
||||||
|
**Configuration Approach**:
|
||||||
|
1. Use patterns as starting point
|
||||||
|
2. Adapt to your use case
|
||||||
|
3. Validate configuration
|
||||||
|
4. Iterate based on errors
|
||||||
|
5. Deploy when valid
|
||||||
|
|
||||||
|
**Related Files**:
|
||||||
|
- **[SKILL.md](SKILL.md)** - Configuration workflow and philosophy
|
||||||
|
- **[DEPENDENCIES.md](DEPENDENCIES.md)** - Property dependency rules
|
||||||
364
docs/skills/n8n-automation/node-configuration/README.md
Normal file
364
docs/skills/n8n-automation/node-configuration/README.md
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
# n8n Node Configuration
|
||||||
|
|
||||||
|
Expert guidance for operation-aware node configuration with property dependencies.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Skill Name**: n8n Node Configuration
|
||||||
|
**Priority**: Medium
|
||||||
|
**Purpose**: Teach operation-aware configuration with progressive discovery and dependency awareness
|
||||||
|
|
||||||
|
## The Problem This Solves
|
||||||
|
|
||||||
|
Node configuration patterns:
|
||||||
|
|
||||||
|
- get_node_essentials is the primary discovery tool (18s avg from search → essentials)
|
||||||
|
- 91.7% success rate with essentials-based configuration
|
||||||
|
- 56 seconds average between configuration edits
|
||||||
|
|
||||||
|
**Key insight**: Most configurations only need essentials, not full schema!
|
||||||
|
|
||||||
|
## What This Skill Teaches
|
||||||
|
|
||||||
|
### Core Concepts
|
||||||
|
|
||||||
|
1. **Operation-Aware Configuration**
|
||||||
|
- Resource + operation determine required fields
|
||||||
|
- Different operations = different requirements
|
||||||
|
- Always check requirements when changing operation
|
||||||
|
|
||||||
|
2. **Property Dependencies**
|
||||||
|
- Fields appear/disappear based on other field values
|
||||||
|
- displayOptions control visibility
|
||||||
|
- Conditional required fields
|
||||||
|
- Understanding dependency chains
|
||||||
|
|
||||||
|
3. **Progressive Discovery**
|
||||||
|
- Start with get_node_essentials (91.7% success)
|
||||||
|
- Escalate to get_property_dependencies if needed
|
||||||
|
- Use get_node_info only when necessary
|
||||||
|
- Right tool for right job
|
||||||
|
|
||||||
|
4. **Configuration Workflow**
|
||||||
|
- Identify → Discover → Configure → Validate → Iterate
|
||||||
|
- Average 2-3 validation cycles
|
||||||
|
- Read errors for dependency hints
|
||||||
|
- 56 seconds between edits average
|
||||||
|
|
||||||
|
5. **Common Patterns**
|
||||||
|
- Resource/operation nodes (Slack, Sheets)
|
||||||
|
- HTTP-based nodes (HTTP Request, Webhook)
|
||||||
|
- Database nodes (Postgres, MySQL)
|
||||||
|
- Conditional logic nodes (IF, Switch)
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
n8n-node-configuration/
|
||||||
|
├── SKILL.md (692 lines)
|
||||||
|
│ Main configuration guide
|
||||||
|
│ - Configuration philosophy (progressive disclosure)
|
||||||
|
│ - Core concepts (operation-aware, dependencies)
|
||||||
|
│ - Configuration workflow (8-step process)
|
||||||
|
│ - get_node_essentials vs get_node_info
|
||||||
|
│ - Property dependencies deep dive
|
||||||
|
│ - Common node patterns (4 categories)
|
||||||
|
│ - Operation-specific examples
|
||||||
|
│ - Conditional requirements
|
||||||
|
│ - Anti-patterns and best practices
|
||||||
|
│
|
||||||
|
├── DEPENDENCIES.md (671 lines)
|
||||||
|
│ Property dependencies reference
|
||||||
|
│ - displayOptions mechanism
|
||||||
|
│ - show vs hide rules
|
||||||
|
│ - Multiple conditions (AND logic)
|
||||||
|
│ - Multiple values (OR logic)
|
||||||
|
│ - 4 common dependency patterns
|
||||||
|
│ - Using get_property_dependencies
|
||||||
|
│ - Complex dependency examples
|
||||||
|
│ - Nested dependencies
|
||||||
|
│ - Auto-sanitization interaction
|
||||||
|
│ - Troubleshooting guide
|
||||||
|
│ - Advanced patterns
|
||||||
|
│
|
||||||
|
├── OPERATION_PATTERNS.md (783 lines)
|
||||||
|
│ Common configurations by node type
|
||||||
|
│ - HTTP Request (GET/POST/PUT/DELETE)
|
||||||
|
│ - Webhook (basic/auth/response)
|
||||||
|
│ - Slack (post/update/create)
|
||||||
|
│ - Gmail (send/get)
|
||||||
|
│ - Postgres (query/insert/update)
|
||||||
|
│ - Set (values/mapping)
|
||||||
|
│ - Code (per-item/all-items)
|
||||||
|
│ - IF (string/number/boolean)
|
||||||
|
│ - Switch (rules/fallback)
|
||||||
|
│ - OpenAI (chat completion)
|
||||||
|
│ - Schedule (daily/interval/cron)
|
||||||
|
│ - Gotchas and tips for each
|
||||||
|
│
|
||||||
|
└── README.md (this file)
|
||||||
|
Skill metadata and statistics
|
||||||
|
```
|
||||||
|
|
||||||
|
**Total**: ~2,146 lines across 4 files + 4 evaluations
|
||||||
|
|
||||||
|
## Usage Statistics
|
||||||
|
|
||||||
|
Configuration metrics:
|
||||||
|
|
||||||
|
| Metric | Value | Insight |
|
||||||
|
|---|---|---|
|
||||||
|
| get_node_essentials | Primary tool | Most popular discovery pattern |
|
||||||
|
| Success rate (essentials) | 91.7% | Essentials sufficient for most |
|
||||||
|
| Avg time search→essentials | 18 seconds | Fast discovery workflow |
|
||||||
|
| Avg time between edits | 56 seconds | Iterative configuration |
|
||||||
|
|
||||||
|
## Tool Usage Pattern
|
||||||
|
|
||||||
|
**Most common discovery pattern**:
|
||||||
|
```
|
||||||
|
search_nodes → get_node_essentials (18s average)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configuration cycle**:
|
||||||
|
```
|
||||||
|
get_node_essentials → configure → validate → iterate (56s avg per edit)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Insights
|
||||||
|
|
||||||
|
### 1. Progressive Disclosure Works
|
||||||
|
|
||||||
|
**91.7% success rate** with get_node_essentials proves most configurations don't need full schema.
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
1. Start with essentials
|
||||||
|
2. Escalate to dependencies if stuck
|
||||||
|
3. Use full schema only when necessary
|
||||||
|
|
||||||
|
### 2. Operations Determine Requirements
|
||||||
|
|
||||||
|
**Same node, different operation = different requirements**
|
||||||
|
|
||||||
|
Example: Slack message
|
||||||
|
- `operation="post"` → needs channel + text
|
||||||
|
- `operation="update"` → needs messageId + text (different!)
|
||||||
|
|
||||||
|
### 3. Dependencies Control Visibility
|
||||||
|
|
||||||
|
**Fields appear/disappear based on other values**
|
||||||
|
|
||||||
|
Example: HTTP Request
|
||||||
|
- `method="GET"` → body hidden
|
||||||
|
- `method="POST"` + `sendBody=true` → body required
|
||||||
|
|
||||||
|
### 4. Configuration is Iterative
|
||||||
|
|
||||||
|
**Average 56 seconds between edits** shows configuration is iterative, not one-shot.
|
||||||
|
|
||||||
|
**Normal workflow**:
|
||||||
|
1. Configure minimal
|
||||||
|
2. Validate → error
|
||||||
|
3. Add missing field
|
||||||
|
4. Validate → error
|
||||||
|
5. Adjust value
|
||||||
|
6. Validate → valid ✅
|
||||||
|
|
||||||
|
### 5. Common Gotchas Exist
|
||||||
|
|
||||||
|
**Top 5 gotchas** from patterns:
|
||||||
|
1. Webhook data under `$json.body` (not `$json`)
|
||||||
|
2. POST needs `sendBody: true`
|
||||||
|
3. Slack channel format (`#name`)
|
||||||
|
4. SQL parameterized queries (injection prevention)
|
||||||
|
5. Timezone must be explicit (schedule nodes)
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Example 1: Basic Configuration Flow
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Step 1: Get essentials
|
||||||
|
const info = get_node_essentials({
|
||||||
|
nodeType: "nodes-base.slack"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2: Configure for operation
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general",
|
||||||
|
"text": "Hello!"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Validate
|
||||||
|
validate_node_operation({...});
|
||||||
|
// ✅ Valid!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Handling Dependencies
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Step 1: Configure HTTP POST
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/create"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Validate → Error: "sendBody required"
|
||||||
|
// Step 3: Check dependencies
|
||||||
|
get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
// Shows: body visible when sendBody=true
|
||||||
|
|
||||||
|
// Step 4: Fix
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/create",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {...}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ✅ Valid!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Operation Change
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Initial config (post operation)
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general",
|
||||||
|
"text": "Hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change operation
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update", // Changed!
|
||||||
|
// Need to check new requirements
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get essentials for update operation
|
||||||
|
get_node_essentials({nodeType: "nodes-base.slack"});
|
||||||
|
// Shows: messageId required, channel optional
|
||||||
|
|
||||||
|
// Correct config
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update",
|
||||||
|
"messageId": "1234567890.123456",
|
||||||
|
"text": "Updated"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## When This Skill Activates
|
||||||
|
|
||||||
|
**Trigger phrases**:
|
||||||
|
- "how to configure"
|
||||||
|
- "what fields are required"
|
||||||
|
- "property dependencies"
|
||||||
|
- "get_node_essentials vs get_node_info"
|
||||||
|
- "operation-specific"
|
||||||
|
- "field not visible"
|
||||||
|
|
||||||
|
**Common scenarios**:
|
||||||
|
- Configuring new nodes
|
||||||
|
- Understanding required fields
|
||||||
|
- Field appears/disappears unexpectedly
|
||||||
|
- Choosing between discovery tools
|
||||||
|
- Switching operations
|
||||||
|
- Learning common patterns
|
||||||
|
|
||||||
|
## Integration with Other Skills
|
||||||
|
|
||||||
|
### Works With:
|
||||||
|
- **n8n MCP Tools Expert** - How to call discovery tools correctly
|
||||||
|
- **n8n Validation Expert** - Interpret missing_required errors
|
||||||
|
- **n8n Expression Syntax** - Configure expression fields
|
||||||
|
- **n8n Workflow Patterns** - Apply patterns with proper node config
|
||||||
|
|
||||||
|
### Complementary:
|
||||||
|
- Use MCP Tools Expert to learn tool selection
|
||||||
|
- Use Validation Expert to fix configuration errors
|
||||||
|
- Use Expression Syntax for dynamic field values
|
||||||
|
- Use Workflow Patterns to understand node relationships
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
**Evaluations**: 4 test scenarios
|
||||||
|
|
||||||
|
1. **eval-001-property-dependencies.json**
|
||||||
|
- Tests understanding of displayOptions
|
||||||
|
- Guides to get_property_dependencies
|
||||||
|
- Explains conditional requirements
|
||||||
|
|
||||||
|
2. **eval-002-operation-specific-config.json**
|
||||||
|
- Tests operation-aware configuration
|
||||||
|
- Identifies resource + operation pattern
|
||||||
|
- References OPERATION_PATTERNS.md
|
||||||
|
|
||||||
|
3. **eval-003-conditional-fields.json**
|
||||||
|
- Tests unary vs binary operators
|
||||||
|
- Explains singleValue dependency
|
||||||
|
- Mentions auto-sanitization
|
||||||
|
|
||||||
|
4. **eval-004-essentials-vs-info.json**
|
||||||
|
- Tests tool selection knowledge
|
||||||
|
- Explains progressive disclosure
|
||||||
|
- Provides success rate statistics
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
**Before this skill**:
|
||||||
|
- Using get_node_info for everything (slow, overwhelming)
|
||||||
|
- Not understanding property dependencies
|
||||||
|
- Confused when fields appear/disappear
|
||||||
|
- Not aware of operation-specific requirements
|
||||||
|
- Trial and error configuration
|
||||||
|
|
||||||
|
**After this skill**:
|
||||||
|
- Start with get_node_essentials (91.7% success)
|
||||||
|
- Understand displayOptions mechanism
|
||||||
|
- Predict field visibility based on dependencies
|
||||||
|
- Check requirements when changing operations
|
||||||
|
- Systematic configuration approach
|
||||||
|
- Know common patterns by node type
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
**Node types covered**: Top 20 most-used nodes
|
||||||
|
|
||||||
|
| Category | Nodes | Coverage |
|
||||||
|
|---|---|---|
|
||||||
|
| HTTP/API | HTTP Request, Webhook | Complete |
|
||||||
|
| Communication | Slack, Gmail | Common operations |
|
||||||
|
| Database | Postgres, MySQL | CRUD operations |
|
||||||
|
| Transform | Set, Code | All modes |
|
||||||
|
| Conditional | IF, Switch | All operator types |
|
||||||
|
| AI | OpenAI | Chat completion |
|
||||||
|
| Schedule | Schedule Trigger | All modes |
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- **n8n-mcp MCP Server**: Provides discovery tools
|
||||||
|
- **n8n Node API**: get_node_essentials, get_property_dependencies, get_node_info
|
||||||
|
- **n8n Schema**: displayOptions mechanism, property definitions
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **v1.0** (2025-10-20): Initial implementation
|
||||||
|
- SKILL.md with configuration workflow
|
||||||
|
- DEPENDENCIES.md with displayOptions deep dive
|
||||||
|
- OPERATION_PATTERNS.md with 20+ node patterns
|
||||||
|
- 4 evaluation scenarios
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
|
||||||
|
|
||||||
|
Part of the n8n-skills meta-skill collection.
|
||||||
774
docs/skills/n8n-automation/node-configuration/SKILL.md
Normal file
774
docs/skills/n8n-automation/node-configuration/SKILL.md
Normal file
@@ -0,0 +1,774 @@
|
|||||||
|
---
|
||||||
|
name: n8n-node-configuration
|
||||||
|
description: Operation-aware node configuration guidance. Use when configuring nodes, understanding property dependencies, determining required fields, choosing between get_node_essentials and get_node_info, or learning common configuration patterns by node type.
|
||||||
|
---
|
||||||
|
|
||||||
|
# n8n Node Configuration
|
||||||
|
|
||||||
|
Expert guidance for operation-aware node configuration with property dependencies.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Philosophy
|
||||||
|
|
||||||
|
**Progressive disclosure**: Start minimal, add complexity as needed
|
||||||
|
|
||||||
|
Configuration best practices:
|
||||||
|
- get_node_essentials is the most used discovery pattern
|
||||||
|
- 56 seconds average between configuration edits
|
||||||
|
- 91.7% success rate with essentials-based configuration
|
||||||
|
|
||||||
|
**Key insight**: Most configurations need only essentials, not full schema!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
|
||||||
|
### 1. Operation-Aware Configuration
|
||||||
|
|
||||||
|
**Not all fields are always required** - it depends on operation!
|
||||||
|
|
||||||
|
**Example**: Slack node
|
||||||
|
```javascript
|
||||||
|
// For operation='post'
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general", // Required for post
|
||||||
|
"text": "Hello!" // Required for post
|
||||||
|
}
|
||||||
|
|
||||||
|
// For operation='update'
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update",
|
||||||
|
"messageId": "123", // Required for update (different!)
|
||||||
|
"text": "Updated!" // Required for update
|
||||||
|
// channel NOT required for update
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key**: Resource + operation determine which fields are required!
|
||||||
|
|
||||||
|
### 2. Property Dependencies
|
||||||
|
|
||||||
|
**Fields appear/disappear based on other field values**
|
||||||
|
|
||||||
|
**Example**: HTTP Request node
|
||||||
|
```javascript
|
||||||
|
// When method='GET'
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://api.example.com"
|
||||||
|
// sendBody not shown (GET doesn't have body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When method='POST'
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com",
|
||||||
|
"sendBody": true, // Now visible!
|
||||||
|
"body": { // Required when sendBody=true
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {...}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mechanism**: displayOptions control field visibility
|
||||||
|
|
||||||
|
### 3. Progressive Discovery
|
||||||
|
|
||||||
|
**Use the right tool for the right job**:
|
||||||
|
|
||||||
|
1. **get_node_essentials** (91.7% success rate)
|
||||||
|
- Quick overview
|
||||||
|
- Required fields
|
||||||
|
- Common options
|
||||||
|
- **Use first** - covers 90% of needs
|
||||||
|
|
||||||
|
2. **get_property_dependencies** (for complex nodes)
|
||||||
|
- Shows what fields depend on others
|
||||||
|
- Reveals conditional requirements
|
||||||
|
- Use when essentials isn't enough
|
||||||
|
|
||||||
|
3. **get_node_info** (full schema)
|
||||||
|
- Complete documentation
|
||||||
|
- All possible fields
|
||||||
|
- Use when essentials + dependencies insufficient
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Workflow
|
||||||
|
|
||||||
|
### Standard Process
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Identify node type and operation
|
||||||
|
↓
|
||||||
|
2. Use get_node_essentials
|
||||||
|
↓
|
||||||
|
3. Configure required fields
|
||||||
|
↓
|
||||||
|
4. Validate configuration
|
||||||
|
↓
|
||||||
|
5. If dependencies unclear → get_property_dependencies
|
||||||
|
↓
|
||||||
|
6. Add optional fields as needed
|
||||||
|
↓
|
||||||
|
7. Validate again
|
||||||
|
↓
|
||||||
|
8. Deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Configuring HTTP Request
|
||||||
|
|
||||||
|
**Step 1**: Identify what you need
|
||||||
|
```javascript
|
||||||
|
// Goal: POST JSON to API
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2**: Get essentials
|
||||||
|
```javascript
|
||||||
|
const info = get_node_essentials({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns: method, url, sendBody, body, authentication required/optional
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3**: Minimal config
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/create",
|
||||||
|
"authentication": "none"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4**: Validate
|
||||||
|
```javascript
|
||||||
|
validate_node_operation({
|
||||||
|
nodeType: "nodes-base.httpRequest",
|
||||||
|
config,
|
||||||
|
profile: "runtime"
|
||||||
|
});
|
||||||
|
// → Error: "sendBody required for POST"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 5**: Add required field
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/create",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 6**: Validate again
|
||||||
|
```javascript
|
||||||
|
validate_node_operation({...});
|
||||||
|
// → Error: "body required when sendBody=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 7**: Complete configuration
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/create",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true,
|
||||||
|
"body": {
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "={{$json.name}}",
|
||||||
|
"email": "={{$json.email}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 8**: Final validation
|
||||||
|
```javascript
|
||||||
|
validate_node_operation({...});
|
||||||
|
// → Valid! ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## get_node_essentials vs get_node_info
|
||||||
|
|
||||||
|
### Use get_node_essentials When:
|
||||||
|
|
||||||
|
**✅ Starting configuration** (91.7% success rate)
|
||||||
|
```javascript
|
||||||
|
get_node_essentials({
|
||||||
|
nodeType: "nodes-base.slack"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**:
|
||||||
|
- Required fields
|
||||||
|
- Common options
|
||||||
|
- Basic examples
|
||||||
|
- Operation list
|
||||||
|
|
||||||
|
**Fast**: ~18 seconds average (from search → essentials)
|
||||||
|
|
||||||
|
### Use get_node_info When:
|
||||||
|
|
||||||
|
**✅ Essentials insufficient**
|
||||||
|
```javascript
|
||||||
|
get_node_info({
|
||||||
|
nodeType: "nodes-base.slack"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**:
|
||||||
|
- Full schema
|
||||||
|
- All properties
|
||||||
|
- Complete documentation
|
||||||
|
- Advanced options
|
||||||
|
|
||||||
|
**Slower**: More data to process
|
||||||
|
|
||||||
|
### Decision Tree
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Starting new node config? │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ YES → get_node_essentials │
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Essentials has what you need? │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ YES → Configure with essentials │
|
||||||
|
│ NO → Continue │
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Need dependency info? │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ YES → get_property_dependencies │
|
||||||
|
│ NO → Continue │
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Still need more details? │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ YES → get_node_info │
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Property Dependencies Deep Dive
|
||||||
|
|
||||||
|
### displayOptions Mechanism
|
||||||
|
|
||||||
|
**Fields have visibility rules**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"displayOptions": {
|
||||||
|
"show": {
|
||||||
|
"sendBody": [true],
|
||||||
|
"method": ["POST", "PUT", "PATCH"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Translation**: "body" field shows when:
|
||||||
|
- sendBody = true AND
|
||||||
|
- method = POST, PUT, or PATCH
|
||||||
|
|
||||||
|
### Common Dependency Patterns
|
||||||
|
|
||||||
|
#### Pattern 1: Boolean Toggle
|
||||||
|
|
||||||
|
**Example**: HTTP Request sendBody
|
||||||
|
```javascript
|
||||||
|
// sendBody controls body visibility
|
||||||
|
{
|
||||||
|
"sendBody": true // → body field appears
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 2: Operation Switch
|
||||||
|
|
||||||
|
**Example**: Slack resource/operation
|
||||||
|
```javascript
|
||||||
|
// Different operations → different fields
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post"
|
||||||
|
// → Shows: channel, text, attachments, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update"
|
||||||
|
// → Shows: messageId, text (different fields!)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pattern 3: Type Selection
|
||||||
|
|
||||||
|
**Example**: IF node conditions
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"operation": "contains"
|
||||||
|
// → Shows: value1, value2
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "equals"
|
||||||
|
// → Shows: value1, value2, different operators
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using get_property_dependencies
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```javascript
|
||||||
|
const deps = get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns dependency tree
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"body": {
|
||||||
|
"shows_when": {
|
||||||
|
"sendBody": [true],
|
||||||
|
"method": ["POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"queryParameters": {
|
||||||
|
"shows_when": {
|
||||||
|
"sendQuery": [true]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use this when**: Validation fails and you don't understand why field is missing/required
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Node Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Resource/Operation Nodes
|
||||||
|
|
||||||
|
**Examples**: Slack, Google Sheets, Airtable
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "<entity>", // What type of thing
|
||||||
|
"operation": "<action>", // What to do with it
|
||||||
|
// ... operation-specific fields
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to configure**:
|
||||||
|
1. Choose resource
|
||||||
|
2. Choose operation
|
||||||
|
3. Use get_node_essentials to see operation-specific requirements
|
||||||
|
4. Configure required fields
|
||||||
|
|
||||||
|
### Pattern 2: HTTP-Based Nodes
|
||||||
|
|
||||||
|
**Examples**: HTTP Request, Webhook
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "<HTTP_METHOD>",
|
||||||
|
"url": "<endpoint>",
|
||||||
|
"authentication": "<type>",
|
||||||
|
// ... method-specific fields
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- POST/PUT/PATCH → sendBody available
|
||||||
|
- sendBody=true → body required
|
||||||
|
- authentication != "none" → credentials required
|
||||||
|
|
||||||
|
### Pattern 3: Database Nodes
|
||||||
|
|
||||||
|
**Examples**: Postgres, MySQL, MongoDB
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"operation": "<query|insert|update|delete>",
|
||||||
|
// ... operation-specific fields
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- operation="executeQuery" → query required
|
||||||
|
- operation="insert" → table + values required
|
||||||
|
- operation="update" → table + values + where required
|
||||||
|
|
||||||
|
### Pattern 4: Conditional Logic Nodes
|
||||||
|
|
||||||
|
**Examples**: IF, Switch, Merge
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"<type>": [
|
||||||
|
{
|
||||||
|
"operation": "<operator>",
|
||||||
|
"value1": "...",
|
||||||
|
"value2": "..." // Only for binary operators
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies**:
|
||||||
|
- Binary operators (equals, contains, etc.) → value1 + value2
|
||||||
|
- Unary operators (isEmpty, isNotEmpty) → value1 only + singleValue: true
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Operation-Specific Configuration
|
||||||
|
|
||||||
|
### Slack Node Examples
|
||||||
|
|
||||||
|
#### Post Message
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general", // Required
|
||||||
|
"text": "Hello!", // Required
|
||||||
|
"attachments": [], // Optional
|
||||||
|
"blocks": [] // Optional
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Update Message
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update",
|
||||||
|
"messageId": "1234567890", // Required (different from post!)
|
||||||
|
"text": "Updated!", // Required
|
||||||
|
"channel": "#general" // Optional (can be inferred)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create Channel
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"resource": "channel",
|
||||||
|
"operation": "create",
|
||||||
|
"name": "new-channel", // Required
|
||||||
|
"isPrivate": false // Optional
|
||||||
|
// Note: text NOT required for this operation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP Request Node Examples
|
||||||
|
|
||||||
|
#### GET Request
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "predefinedCredentialType",
|
||||||
|
"nodeCredentialType": "httpHeaderAuth",
|
||||||
|
"sendQuery": true, // Optional
|
||||||
|
"queryParameters": { // Shows when sendQuery=true
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"value": "100"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### POST with JSON
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.example.com/users",
|
||||||
|
"authentication": "none",
|
||||||
|
"sendBody": true, // Required for POST
|
||||||
|
"body": { // Required when sendBody=true
|
||||||
|
"contentType": "json",
|
||||||
|
"content": {
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### IF Node Examples
|
||||||
|
|
||||||
|
#### String Comparison (Binary)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.status}}",
|
||||||
|
"operation": "equals",
|
||||||
|
"value2": "active" // Binary: needs value2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Empty Check (Unary)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.email}}",
|
||||||
|
"operation": "isEmpty",
|
||||||
|
// No value2 - unary operator
|
||||||
|
"singleValue": true // Auto-added by sanitization
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Handling Conditional Requirements
|
||||||
|
|
||||||
|
### Example: HTTP Request Body
|
||||||
|
|
||||||
|
**Scenario**: body field required, but only sometimes
|
||||||
|
|
||||||
|
**Rule**:
|
||||||
|
```
|
||||||
|
body is required when:
|
||||||
|
- sendBody = true AND
|
||||||
|
- method IN (POST, PUT, PATCH, DELETE)
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to discover**:
|
||||||
|
```javascript
|
||||||
|
// Option 1: Read validation error
|
||||||
|
validate_node_operation({...});
|
||||||
|
// Error: "body required when sendBody=true"
|
||||||
|
|
||||||
|
// Option 2: Check dependencies
|
||||||
|
get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.httpRequest"
|
||||||
|
});
|
||||||
|
// Shows: body → shows_when: sendBody=[true], method=[POST,PUT,PATCH,DELETE]
|
||||||
|
|
||||||
|
// Option 3: Try minimal config and iterate
|
||||||
|
// Start without body, validation will tell you if needed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: IF Node singleValue
|
||||||
|
|
||||||
|
**Scenario**: singleValue property appears for unary operators
|
||||||
|
|
||||||
|
**Rule**:
|
||||||
|
```
|
||||||
|
singleValue should be true when:
|
||||||
|
- operation IN (isEmpty, isNotEmpty, true, false)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good news**: Auto-sanitization fixes this!
|
||||||
|
|
||||||
|
**Manual check**:
|
||||||
|
```javascript
|
||||||
|
get_property_dependencies({
|
||||||
|
nodeType: "nodes-base.if"
|
||||||
|
});
|
||||||
|
// Shows operator-specific dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Anti-Patterns
|
||||||
|
|
||||||
|
### ❌ Don't: Over-configure Upfront
|
||||||
|
|
||||||
|
**Bad**:
|
||||||
|
```javascript
|
||||||
|
// Adding every possible field
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "...",
|
||||||
|
"sendQuery": false,
|
||||||
|
"sendHeaders": false,
|
||||||
|
"sendBody": false,
|
||||||
|
"timeout": 10000,
|
||||||
|
"ignoreResponseCode": false,
|
||||||
|
// ... 20 more optional fields
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good**:
|
||||||
|
```javascript
|
||||||
|
// Start minimal
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"url": "...",
|
||||||
|
"authentication": "none"
|
||||||
|
}
|
||||||
|
// Add fields only when needed
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ Don't: Skip Validation
|
||||||
|
|
||||||
|
**Bad**:
|
||||||
|
```javascript
|
||||||
|
// Configure and deploy without validating
|
||||||
|
const config = {...};
|
||||||
|
n8n_update_partial_workflow({...}); // YOLO
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good**:
|
||||||
|
```javascript
|
||||||
|
// Validate before deploying
|
||||||
|
const config = {...};
|
||||||
|
const result = validate_node_operation({...});
|
||||||
|
if (result.valid) {
|
||||||
|
n8n_update_partial_workflow({...});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ❌ Don't: Ignore Operation Context
|
||||||
|
|
||||||
|
**Bad**:
|
||||||
|
```javascript
|
||||||
|
// Same config for all Slack operations
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "post",
|
||||||
|
"channel": "#general",
|
||||||
|
"text": "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then switching operation without updating config
|
||||||
|
{
|
||||||
|
"resource": "message",
|
||||||
|
"operation": "update", // Changed
|
||||||
|
"channel": "#general", // Wrong field for update!
|
||||||
|
"text": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good**:
|
||||||
|
```javascript
|
||||||
|
// Check requirements when changing operation
|
||||||
|
get_node_essentials({
|
||||||
|
nodeType: "nodes-base.slack"
|
||||||
|
});
|
||||||
|
// See what update operation needs (messageId, not channel)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### ✅ Do
|
||||||
|
|
||||||
|
1. **Start with get_node_essentials**
|
||||||
|
- 91.7% success rate
|
||||||
|
- Faster than get_node_info
|
||||||
|
- Sufficient for most needs
|
||||||
|
|
||||||
|
2. **Validate iteratively**
|
||||||
|
- Configure → Validate → Fix → Repeat
|
||||||
|
- Average 2-3 iterations is normal
|
||||||
|
- Read validation errors carefully
|
||||||
|
|
||||||
|
3. **Use property dependencies when stuck**
|
||||||
|
- If field seems missing, check dependencies
|
||||||
|
- Understand what controls field visibility
|
||||||
|
- get_property_dependencies reveals rules
|
||||||
|
|
||||||
|
4. **Respect operation context**
|
||||||
|
- Different operations = different requirements
|
||||||
|
- Always check essentials when changing operation
|
||||||
|
- Don't assume configs are transferable
|
||||||
|
|
||||||
|
5. **Trust auto-sanitization**
|
||||||
|
- Operator structure fixed automatically
|
||||||
|
- Don't manually add/remove singleValue
|
||||||
|
- IF/Switch metadata added on save
|
||||||
|
|
||||||
|
### ❌ Don't
|
||||||
|
|
||||||
|
1. **Jump to get_node_info immediately**
|
||||||
|
- Try essentials first
|
||||||
|
- Only escalate if needed
|
||||||
|
- Full schema is overwhelming
|
||||||
|
|
||||||
|
2. **Configure blindly**
|
||||||
|
- Always validate before deploying
|
||||||
|
- Understand why fields are required
|
||||||
|
- Check dependencies for conditional fields
|
||||||
|
|
||||||
|
3. **Copy configs without understanding**
|
||||||
|
- Different operations need different fields
|
||||||
|
- Validate after copying
|
||||||
|
- Adjust for new context
|
||||||
|
|
||||||
|
4. **Manually fix auto-sanitization issues**
|
||||||
|
- Let auto-sanitization handle operator structure
|
||||||
|
- Focus on business logic
|
||||||
|
- Save and let system fix structure
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed References
|
||||||
|
|
||||||
|
For comprehensive guides on specific topics:
|
||||||
|
|
||||||
|
- **[DEPENDENCIES.md](DEPENDENCIES.md)** - Deep dive into property dependencies and displayOptions
|
||||||
|
- **[OPERATION_PATTERNS.md](OPERATION_PATTERNS.md)** - Common configuration patterns by node type
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Configuration Strategy**:
|
||||||
|
1. Start with get_node_essentials (91.7% success)
|
||||||
|
2. Configure required fields for operation
|
||||||
|
3. Validate configuration
|
||||||
|
4. Check dependencies if stuck
|
||||||
|
5. Iterate until valid (avg 2-3 cycles)
|
||||||
|
6. Deploy with confidence
|
||||||
|
|
||||||
|
**Key Principles**:
|
||||||
|
- **Operation-aware**: Different operations = different requirements
|
||||||
|
- **Progressive disclosure**: Start minimal, add as needed
|
||||||
|
- **Dependency-aware**: Understand field visibility rules
|
||||||
|
- **Validation-driven**: Let validation guide configuration
|
||||||
|
|
||||||
|
**Related Skills**:
|
||||||
|
- **n8n MCP Tools Expert** - How to use discovery tools correctly
|
||||||
|
- **n8n Validation Expert** - Interpret validation errors
|
||||||
|
- **n8n Expression Syntax** - Configure expression fields
|
||||||
|
- **n8n Workflow Patterns** - Apply patterns with proper configuration
|
||||||
Reference in New Issue
Block a user