Claude Agent Skill · by Czlonkowski

N8n Code Javascript

This one's a lifesaver if you're building n8n workflows that need custom JavaScript logic. It handles the tricky bits like properly accessing webhook data under

Install
Terminal · npx
$npx skills add https://github.com/czlonkowski/n8n-skills --skill n8n-code-javascript
Works with Paperclip

How N8n Code Javascript fits into a Paperclip company.

N8n Code Javascript drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md784 lines
Expand
---name: n8n-code-javascriptdescription: 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, choosing between Code node modes, or doing any custom data transformation in n8n. Always use this skill when a workflow needs a Code node — whether for data aggregation, filtering, API calls, format conversion, batch processing logic, or any custom JavaScript. Covers SplitInBatches loop patterns, cross-iteration data, pairedItem, and real-world production patterns.--- # JavaScript Code Node Expert guidance for writing JavaScript code in n8n Code nodes. --- ## Quick Start ```javascript// Basic template for Code nodesconst items = $input.all(); // Process dataconst 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: {...}}]` format4. **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 itemsconst 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 itemconst 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 nodeconst allItems = $input.all(); // Filter, map, reduce as neededconst 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 onlyconst 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 nodeconst 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 undefinedconst name = $json.name;const email = $json.email; // ✅ CORRECT - Webhook data is under .bodyconst name = $json.body.name;const email = $json.body.email; // Or with $inputconst 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 resultreturn [{  json: {    field1: value1,    field2: value2  }}]; // ✅ Multiple resultsreturn [  {json: {id: 1, data: 'first'}},  {json: {id: 2, data: 'second'}}]; // ✅ Transformed arrayconst 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 returnif (shouldProcess) {  return [{json: processedData}];} else {  return [];}``` ### Incorrect Return Formats ```javascript// ❌ WRONG: Object without array wrapperreturn {  json: {field: value}}; // ❌ WRONG: Array without json wrapperreturn [{field: value}]; // ❌ WRONG: Plain stringreturn "processed"; // ❌ WRONG: Raw data without mappingreturn $input.all();  // Missing .map() // ❌ WRONG: Incomplete structurereturn [{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 AggregationCombine data from multiple APIs, webhooks, or nodes ```javascriptconst 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 RegexExtract patterns, mentions, or keywords from text ```javascriptconst 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 & EnrichmentMap fields, normalize formats, add computed fields ```javascriptconst 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 & RankingSort and limit results ```javascriptconst 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 & ReportingSum, count, group data ```javascriptconst 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 statementconst items = $input.all();// ... processing code ...// Forgot to return! // ✅ CORRECT: Always return dataconst items = $input.all();// ... processing ...return items.map(item => ({json: item.json}));``` ### #2: Expression Syntax Confusion ```javascript// ❌ WRONG: Using n8n expression syntax in codeconst value = "{{ $json.field }}"; // ✅ CORRECT: Use JavaScript template literalsconst value = `${$json.field}`; // ✅ CORRECT: Direct accessconst value = $input.first().json.field;``` ### #3: Incorrect Return Wrapper ```javascript// ❌ WRONG: Returning object instead of arrayreturn {json: {result: 'success'}}; // ✅ CORRECT: Array wrapper requiredreturn [{json: {result: 'success'}}];``` ### #4: Missing Null Checks ```javascript// ❌ WRONG: Crashes if field doesn't existconst value = item.json.user.email; // ✅ CORRECT: Safe access with optional chainingconst value = item.json?.user?.email || 'no-email@example.com'; // ✅ CORRECT: Guard clauseif (!item.json.user) {  return [];}const value = item.json.user.email;``` ### #5: Webhook Body Nesting ```javascript// ❌ WRONG: Direct access to webhook dataconst email = $json.email; // ✅ CORRECT: Webhook data under .bodyconst 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: ```javascriptconst 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 timeconst now = DateTime.now(); // Format datesconst formatted = now.toFormat('yyyy-MM-dd');const iso = now.toISO(); // Date arithmeticconst 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: ```javascriptconst data = $input.first().json; // Filter arrayconst adults = $jmespath(data, 'users[?age >= `18`]'); // Extract fieldsconst 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 ```javascriptconst items = $input.all(); // Check if data existsif (!items || items.length === 0) {  return [];} // Validate structureif (!items[0].json) {  return [{json: {error: 'Invalid input format'}}];} // Continue processing...``` ### 2. Use Try-Catch for Error Handling ```javascripttry {  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 approachconst processed = $input.all()  .filter(item => item.json.valid)  .map(item => ({json: {id: item.json.id}})); // ❌ SLOWER: Manual loopconst 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 processingconst processed = $input.all()  .filter(item => item.json.status === 'active')  // Reduce dataset first  .map(item => expensiveTransformation(item));  // Then transform // ❌ WASTEFUL: Transform everything, then filterconst processed = $input.all()  .map(item => expensiveTransformation(item))  // Wastes CPU  .filter(item => item.json.status === 'active');``` ### 5. Use Descriptive Variable Names ```javascript// ✅ GOOD: Clear intentconst activeUsers = $input.all().filter(item => item.json.active);const totalRevenue = activeUsers.reduce((sum, user) => sum + user.json.revenue, 0); // ❌ BAD: Unclear purposeconst 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 consoleconst items = $input.all();console.log(`Processing ${items.length} items`); for (const item of items) {  console.log('Item data:', item.json);  // Process...} return result;``` --- ## Production Gotchas Hard-won lessons from real-world n8n workflow deployments: ### SplitInBatches Loop Semantics The SplitInBatches node has two outputs — and the naming is counterintuitive:- `main[0]` = **done** — fires ONCE after all batches are processed- `main[1]` = **each batch** — fires for every batch (this is the loop body) Always add a **Limit 1** node after the done output before downstream processing, as a safety against edge cases where done fires with extra items. ### Cross-Iteration Data Accumulation (CRITICAL) After a SplitInBatches loop, `$('Node Inside Loop').all()` returns **ONLY the last iteration's items**, not cumulative results. This silently drops data from all but the final batch. **Fix**: Use workflow static data to accumulate across iterations: ```javascript// BEFORE the loop (reset accumulator):const staticData = $getWorkflowStaticData('global');staticData.results = [];return $input.all(); // INSIDE the loop body (accumulate):const staticData = $getWorkflowStaticData('global');const results = [];for (const item of $input.all()) {  const processed = { /* ... */ };  results.push({ json: processed });  staticData.results.push(processed);}return results; // AFTER the loop (read accumulated data):const staticData = $getWorkflowStaticData('global');const allResults = staticData.results || [];// Now aggregate across ALL iterations``` ### pairedItem for New Output Items When creating new items that don't map 1:1 to input items, include `pairedItem` — otherwise downstream Set nodes fail with `paired_item_no_info`: ```javascriptconst results = [];for (let i = 0; i < $input.all().length; i++) {  const item = $input.all()[i];  results.push({    json: { /* new data */ },    pairedItem: { item: i }  });}return results;``` ### Correct Node Reference Syntax ```javascript// ❌ WRONG - .json directly on node referenceconst data = $('HTTP Request').json; // ✅ CORRECT - call .first() then access .jsonconst data = $('HTTP Request').first().json; // ✅ Also correct - get all itemsconst allData = $('HTTP Request').all();``` ### Float Precision for Price/Currency Comparison When comparing prices or currency values, floating point noise can cause false positives. Round to cents: ```javascript// ❌ Unreliable - float comparisonif (newPrice !== oldPrice) { /* triggers on noise */ } // ✅ Reliable - compare at cent levelif (Math.round(newPrice * 100) !== Math.round(oldPrice * 100)) {  // Real price change detected}``` --- ## 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({nodeType: "nodes-base.code"})`- Validate code: `validate_node({nodeType: "nodes-base.code", config: {...}})` **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.