mirror of
https://github.com/czlonkowski/n8n-skills.git
synced 2026-03-16 23:43:08 +00:00
feat: Add n8n-code-javascript skill (Skill #6)
Complete expert guidance for writing JavaScript in n8n Code nodes.
Files added (6 skill files + 5 evaluations):
- SKILL.md (699 lines) - Quick start, mode selection, data access overview
- DATA_ACCESS.md (782 lines) - Complete data access patterns
- COMMON_PATTERNS.md (1,110 lines) - 10 production-tested patterns
- ERROR_PATTERNS.md (763 lines) - Top 5 errors covering 62%+ of failures
- BUILTIN_FUNCTIONS.md (764 lines) - Complete built-in function reference
- README.md (350 lines) - Skill metadata and overview
Total: 4,468 lines across 6 files + 5 evaluation scenarios
Key features:
- Data access patterns: $input.all(), $input.first(), $input.item
- Critical gotcha highlighted: Webhook data under .body
- Return format emphasized: [{json: {...}}]
- Top 5 error patterns with solutions (38%, 8%, 5%, 6% of failures)
- 10 production-tested patterns (multi-source aggregation, regex filtering, etc.)
- Complete built-in function reference ($helpers.httpRequest, DateTime, $jmespath)
- Mode selection guide (All Items vs Each Item)
Evaluations (5):
- eval-001: Webhook body gotcha (most common mistake)
- eval-002: Return format error (missing array wrapper)
- eval-003: HTTP requests with $helpers.httpRequest()
- eval-004: Aggregation pattern with reduce()
- eval-005: Expression syntax confusion (using {{}} in code)
Documentation updates:
- README.md: Updated from 5 to 7 skills
- plugin.json: Added code/javascript/python keywords
- marketplace.json: Updated description to reflect 7 skills
Ready for: Production use
🤖 Generated with Claude Code
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp-skills",
|
"name": "n8n-mcp-skills",
|
||||||
"source": "./",
|
"source": "./",
|
||||||
"description": "Complete bundle: 5 expert skills for building flawless n8n workflows using n8n-mcp MCP server. Includes skills for expression syntax, MCP tools usage, workflow patterns, validation, and node configuration.",
|
"description": "Complete bundle: 7 expert skills for building flawless n8n workflows using n8n-mcp MCP server. Includes skills for expression syntax, MCP tools usage, workflow patterns, validation, node configuration, JavaScript code, and Python code.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Romuald Członkowski",
|
"name": "Romuald Członkowski",
|
||||||
@@ -23,6 +23,9 @@
|
|||||||
"automation",
|
"automation",
|
||||||
"validation",
|
"validation",
|
||||||
"expressions",
|
"expressions",
|
||||||
|
"code",
|
||||||
|
"javascript",
|
||||||
|
"python",
|
||||||
"skills"
|
"skills"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/czlonkowski/n8n-skills",
|
"homepage": "https://github.com/czlonkowski/n8n-skills",
|
||||||
|
|||||||
@@ -13,7 +13,10 @@
|
|||||||
"automation",
|
"automation",
|
||||||
"mcp",
|
"mcp",
|
||||||
"validation",
|
"validation",
|
||||||
"expressions"
|
"expressions",
|
||||||
|
"code",
|
||||||
|
"javascript",
|
||||||
|
"python"
|
||||||
],
|
],
|
||||||
"repository": "https://github.com/czlonkowski/n8n-skills",
|
"repository": "https://github.com/czlonkowski/n8n-skills",
|
||||||
"homepage": "https://github.com/czlonkowski/n8n-skills"
|
"homepage": "https://github.com/czlonkowski/n8n-skills"
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -28,7 +28,7 @@ These skills solve these problems by teaching Claude:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 The 5 Skills
|
## 📚 The 7 Skills
|
||||||
|
|
||||||
### 1. **n8n Expression Syntax**
|
### 1. **n8n Expression Syntax**
|
||||||
Teaches correct n8n expression syntax and common patterns.
|
Teaches correct n8n expression syntax and common patterns.
|
||||||
@@ -90,6 +90,32 @@ Operation-aware node configuration guidance.
|
|||||||
- AI connection types (8 types for AI Agent workflows)
|
- AI connection types (8 types for AI Agent workflows)
|
||||||
- Common configuration patterns
|
- Common configuration patterns
|
||||||
|
|
||||||
|
### 6. **n8n Code JavaScript**
|
||||||
|
Write effective JavaScript code in n8n Code nodes.
|
||||||
|
|
||||||
|
**Activates when**: Writing JavaScript in Code nodes, troubleshooting Code node errors, making HTTP requests with $helpers, working with dates.
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- Data access patterns ($input.all(), $input.first(), $input.item)
|
||||||
|
- **Critical gotcha**: Webhook data under `$json.body`
|
||||||
|
- Correct return format: `[{json: {...}}]`
|
||||||
|
- Built-in functions ($helpers.httpRequest(), DateTime, $jmespath())
|
||||||
|
- Top 5 error patterns with solutions (covering 62%+ of failures)
|
||||||
|
- 10 production-tested patterns
|
||||||
|
|
||||||
|
### 7. **n8n Code Python**
|
||||||
|
Write Python code in n8n Code nodes with proper limitations awareness.
|
||||||
|
|
||||||
|
**Activates when**: Writing Python in Code nodes, need to know Python limitations, working with standard library.
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- **Important**: Use JavaScript for 95% of use cases
|
||||||
|
- Python data access (_input, _json, _node)
|
||||||
|
- **Critical limitation**: No external libraries (requests, pandas, numpy)
|
||||||
|
- Standard library reference (json, datetime, re, etc.)
|
||||||
|
- Workarounds for missing libraries
|
||||||
|
- Common Python patterns for n8n
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Installation
|
## 🚀 Installation
|
||||||
@@ -161,6 +187,12 @@ Skills activate **automatically** when relevant queries are detected:
|
|||||||
|
|
||||||
"How do I configure the HTTP Request node?"
|
"How do I configure the HTTP Request node?"
|
||||||
→ Activates: n8n Node Configuration
|
→ Activates: n8n Node Configuration
|
||||||
|
|
||||||
|
"How do I access webhook data in a Code node?"
|
||||||
|
→ Activates: n8n Code JavaScript
|
||||||
|
|
||||||
|
"Can I use pandas in Python Code node?"
|
||||||
|
→ Activates: n8n Code Python
|
||||||
```
|
```
|
||||||
|
|
||||||
### Skills Work Together
|
### Skills Work Together
|
||||||
@@ -170,10 +202,11 @@ When you ask: **"Build and validate a webhook to Slack workflow"**
|
|||||||
1. **n8n Workflow Patterns** identifies webhook processing pattern
|
1. **n8n Workflow Patterns** identifies webhook processing pattern
|
||||||
2. **n8n MCP Tools Expert** searches for webhook and Slack nodes
|
2. **n8n MCP Tools Expert** searches for webhook and Slack nodes
|
||||||
3. **n8n Node Configuration** guides node setup
|
3. **n8n Node Configuration** guides node setup
|
||||||
4. **n8n Expression Syntax** helps with data mapping
|
4. **n8n Code JavaScript** helps process webhook data with proper .body access
|
||||||
5. **n8n Validation Expert** validates the final workflow
|
5. **n8n Expression Syntax** helps with data mapping in other nodes
|
||||||
|
6. **n8n Validation Expert** validates the final workflow
|
||||||
|
|
||||||
All 5 skills compose seamlessly!
|
All skills compose seamlessly!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -238,9 +271,10 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|||||||
|
|
||||||
## 📊 What's Included
|
## 📊 What's Included
|
||||||
|
|
||||||
- **5** complementary skills that work together
|
- **7** complementary skills that work together
|
||||||
- **525+** n8n nodes supported
|
- **525+** n8n nodes supported
|
||||||
- **2,653+** workflow templates for examples
|
- **2,653+** workflow templates for examples
|
||||||
|
- **10** production-tested Code node patterns
|
||||||
- **Comprehensive** error catalogs and troubleshooting guides
|
- **Comprehensive** error catalogs and troubleshooting guides
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"id": "code-js-001",
|
||||||
|
"skills": ["n8n-code-javascript"],
|
||||||
|
"query": "I'm getting undefined when trying to access data from my webhook in a Code node. My code is: const name = $json.name; const email = $json.email; Why isn't this working?",
|
||||||
|
"expected_behavior": [
|
||||||
|
"Activate n8n-code-javascript skill",
|
||||||
|
"Explain that webhook data is nested under the .body property",
|
||||||
|
"Show correct syntax: $json.body.name and $json.body.email",
|
||||||
|
"Reference DATA_ACCESS.md for webhook structure details",
|
||||||
|
"Mention this is the #1 most common mistake",
|
||||||
|
"Optionally show alternative: const webhookData = $json.body; then access webhookData.name"
|
||||||
|
],
|
||||||
|
"expected_content": [
|
||||||
|
"webhook data",
|
||||||
|
".body",
|
||||||
|
"$json.body.name",
|
||||||
|
"DATA_ACCESS.md"
|
||||||
|
],
|
||||||
|
"priority": "high",
|
||||||
|
"notes": "This is the MOST common mistake. Skill must clearly explain .body nesting."
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"id": "code-js-002",
|
||||||
|
"skills": ["n8n-code-javascript"],
|
||||||
|
"query": "My Code node is showing an error: 'Return value must be an array of objects'. My code returns: return {json: {result: 'success', data: processedData}}; What's wrong?",
|
||||||
|
"expected_behavior": [
|
||||||
|
"Activate n8n-code-javascript skill",
|
||||||
|
"Explain the return format must be an ARRAY of objects",
|
||||||
|
"Show correct syntax: return [{json: {result: 'success', data: processedData}}];",
|
||||||
|
"Note the square brackets [] wrapping the object",
|
||||||
|
"Reference ERROR_PATTERNS.md #3 for detailed explanation",
|
||||||
|
"Show multiple items format as well",
|
||||||
|
"Emphasize this is error #3 in top 5 errors (5% of failures)"
|
||||||
|
],
|
||||||
|
"expected_content": [
|
||||||
|
"array",
|
||||||
|
"[{json:",
|
||||||
|
"ERROR_PATTERNS.md",
|
||||||
|
"square brackets"
|
||||||
|
],
|
||||||
|
"priority": "high",
|
||||||
|
"notes": "Return format error is 5% of all failures. Must clearly show array wrapper requirement."
|
||||||
|
}
|
||||||
25
evaluations/code-javascript/eval-003-http-request.json
Normal file
25
evaluations/code-javascript/eval-003-http-request.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"id": "code-js-003",
|
||||||
|
"skills": ["n8n-code-javascript"],
|
||||||
|
"query": "How do I make an HTTP API call from inside a JavaScript Code node in n8n? I need to call an external API with authentication.",
|
||||||
|
"expected_behavior": [
|
||||||
|
"Activate n8n-code-javascript skill",
|
||||||
|
"Explain $helpers.httpRequest() built-in function",
|
||||||
|
"Show complete example with method, url, headers",
|
||||||
|
"Include authentication example (Bearer token or API key)",
|
||||||
|
"Show try-catch error handling pattern",
|
||||||
|
"Reference BUILTIN_FUNCTIONS.md for complete API reference",
|
||||||
|
"Mention this is async, needs await",
|
||||||
|
"Show both GET and POST examples if appropriate"
|
||||||
|
],
|
||||||
|
"expected_content": [
|
||||||
|
"$helpers.httpRequest",
|
||||||
|
"await",
|
||||||
|
"headers",
|
||||||
|
"Authorization",
|
||||||
|
"BUILTIN_FUNCTIONS.md",
|
||||||
|
"try-catch"
|
||||||
|
],
|
||||||
|
"priority": "medium",
|
||||||
|
"notes": "$helpers.httpRequest() is a key built-in function that many users need to learn."
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"id": "code-js-004",
|
||||||
|
"skills": ["n8n-code-javascript"],
|
||||||
|
"query": "I need to sum up all the 'amount' values from multiple items in my Code node. How do I access all items and calculate a total?",
|
||||||
|
"expected_behavior": [
|
||||||
|
"Activate n8n-code-javascript skill",
|
||||||
|
"Show $input.all() to get all items",
|
||||||
|
"Demonstrate reduce() function for summing",
|
||||||
|
"Include null handling (item.json.amount || 0)",
|
||||||
|
"Show complete return format with result",
|
||||||
|
"Reference COMMON_PATTERNS.md for aggregation patterns",
|
||||||
|
"Reference DATA_ACCESS.md for $input.all() details",
|
||||||
|
"Emphasize 'Run Once for All Items' mode",
|
||||||
|
"Optionally show additional aggregations (count, average)"
|
||||||
|
],
|
||||||
|
"expected_content": [
|
||||||
|
"$input.all()",
|
||||||
|
"reduce",
|
||||||
|
"COMMON_PATTERNS.md",
|
||||||
|
"DATA_ACCESS.md",
|
||||||
|
"All Items"
|
||||||
|
],
|
||||||
|
"priority": "high",
|
||||||
|
"notes": "Aggregation is a very common use case. Tests understanding of $input.all() and array methods."
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"id": "code-js-005",
|
||||||
|
"skills": ["n8n-code-javascript"],
|
||||||
|
"query": "I'm trying to access a field in my Code node but it's not working. My code is: const userName = '{{ $json.name }}'; I'm getting the literal string instead of the value. What am I doing wrong?",
|
||||||
|
"expected_behavior": [
|
||||||
|
"Activate n8n-code-javascript skill",
|
||||||
|
"Explain that {{ }} expression syntax does NOT work in Code nodes",
|
||||||
|
"Clarify distinction: {{ }} is for OTHER nodes (Set, IF, HTTP Request)",
|
||||||
|
"Show correct JavaScript syntax: const userName = $json.name;",
|
||||||
|
"Show JavaScript template literals for string interpolation: `Hello ${$json.name}`",
|
||||||
|
"Reference ERROR_PATTERNS.md #2 for detailed explanation",
|
||||||
|
"Emphasize this is error #2 in top 5 errors (8% of failures)",
|
||||||
|
"Provide comparison table of when to use expressions vs JavaScript"
|
||||||
|
],
|
||||||
|
"expected_content": [
|
||||||
|
"{{ }}",
|
||||||
|
"expression syntax",
|
||||||
|
"JavaScript",
|
||||||
|
"$json.name",
|
||||||
|
"template literals",
|
||||||
|
"ERROR_PATTERNS.md",
|
||||||
|
"backticks"
|
||||||
|
],
|
||||||
|
"priority": "high",
|
||||||
|
"notes": "Expression syntax confusion is 8% of failures. Critical to understand Code nodes use JavaScript, not expressions."
|
||||||
|
}
|
||||||
764
skills/n8n-code-javascript/BUILTIN_FUNCTIONS.md
Normal file
764
skills/n8n-code-javascript/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
skills/n8n-code-javascript/COMMON_PATTERNS.md
Normal file
1110
skills/n8n-code-javascript/COMMON_PATTERNS.md
Normal file
File diff suppressed because it is too large
Load Diff
782
skills/n8n-code-javascript/DATA_ACCESS.md
Normal file
782
skills/n8n-code-javascript/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
skills/n8n-code-javascript/ERROR_PATTERNS.md
Normal file
763
skills/n8n-code-javascript/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
skills/n8n-code-javascript/README.md
Normal file
350
skills/n8n-code-javascript/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
skills/n8n-code-javascript/SKILL.md
Normal file
699
skills/n8n-code-javascript/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.
|
||||||
Reference in New Issue
Block a user