Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:17:17 +08:00
commit 6062d3994e
39 changed files with 21748 additions and 0 deletions

View 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&param2=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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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.

View 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.