n8n Code Node: 25 JavaScript Recipes for Non-Developers
n8n Code Node: 25 JavaScript Recipes for Non-Developers
• Logic Workflow Team

n8n Code Node: 25 JavaScript Recipes for Non-Developers

#n8n #code node #javascript #tutorial #automation #recipes

You’re staring at a Code node, cursor blinking, and the JavaScript looks like hieroglyphics. You know the transformation you need. You can picture the data going in and the shape it should come out. But translating that vision into working code feels like trying to write poetry in a language you don’t speak.

This frustration hits thousands of n8n users every day. The Code node sits there, powerful and intimidating, while you wonder if you should just give up and chain together fifteen regular nodes instead.

The Non-Developer’s Dilemma

Most n8n users aren’t developers. They’re marketers automating campaigns, operations managers connecting tools, or founders building their first automation stack. The visual workflow builder is why they chose n8n in the first place.

But certain tasks simply require code. Converting timestamps. Restructuring JSON payloads. Filtering arrays based on multiple conditions. These transformations either need the Code node or become absurdly complex chains of visual nodes.

The problem isn’t intelligence. It’s exposure. JavaScript has quirks that trip up newcomers, from the infamous undefined errors to the strange behavior of equality operators. Without someone handing you working examples, you’re left reverse-engineering Stack Overflow posts that assume you already know half the language.

When the Code Node is Actually Necessary

Before we dive into recipes, let’s be clear about when you actually need the Code node. The n8n community has strong opinions on this topic.

Use the Code node when:

  • Transforming data structures that would require 5+ visual nodes
  • Processing all items into a single aggregated result
  • Performing complex conditional logic with multiple variables
  • The built-in expression system can’t handle your transformation
  • Performance matters and you’re processing large datasets

Skip the Code node when:

  • A native node already does what you need (Check the Filter, Sort, and Merge nodes first)
  • Simple expressions can handle the transformation
  • You’re doing basic field mapping or renaming

What You’ll Learn

This guide gives you 25 copy-paste JavaScript recipes organized by category:

  • Date and Time (5 recipes): Formatting, calculations, timestamp conversions
  • String Manipulation (5 recipes): Cleaning, extracting, and transforming text
  • Array Operations (7 recipes): Filtering, sorting, grouping, and aggregating
  • JSON Transformation (5 recipes): Restructuring, merging, and picking fields
  • API Response Processing (3 recipes): Handling real-world API data

Each recipe includes the problem it solves, working code you can paste directly, and a plain-English explanation of what the code does.

Code Node Fundamentals

Before using any recipe, you need to understand how the Code node actually works. Skip this section at your own peril.

Adding the Code Node

  1. Click the + button in your workflow
  2. Search for “Code”
  3. Select the Code node (not Execute Command)

The Two Run Modes

This single setting causes more confusion than anything else in n8n.

ModeWhen It RunsBest For
Run Once for All ItemsExecutes once, receives all items as arrayAggregations, summaries, batch processing
Run Once for Each ItemExecutes separately for each itemItem-by-item transformations

Default is “Run Once for All Items.” Most recipes in this guide use this mode because it gives you more control.

The Golden Rule: Return Structure

Every Code node must return data in this exact format:

return [
  {
    json: {
      // your data here
    }
  }
];

The outer array contains items. Each item has a json property with your actual data. Forget this structure and your workflow breaks.

Accessing Input Data

MethodReturnsUse When
$input.all()All items as arrayProcessing multiple items together
$input.first()First item onlyOnly need the first result
$jsonCurrent item’s JSONInside “Run Once for Each Item” mode
$('NodeName').all()All items from named nodeAccessing earlier node data

Debugging Your Code

When something breaks, add console.log() statements:

const items = $input.all();
console.log('Input items:', items);
console.log('First item:', items[0]);

Check the browser’s developer console (F12) to see the output. This simple technique saves hours of guessing.

Date and Time Recipes

Date manipulation is the most common Code node task. n8n includes Luxon, a powerful date library, so you don’t need external dependencies.

Recipe 1: Format Date to YYYY-MM-DD

Problem: You have a date in various formats and need it as 2025-12-14.

const items = $input.all();

return items.map(item => {
  const originalDate = item.json.date;
  const formatted = DateTime.fromISO(originalDate).toFormat('yyyy-MM-dd');

  return {
    json: {
      ...item.json,
      formattedDate: formatted
    }
  };
});

How it works: DateTime.fromISO() parses the input date. toFormat() outputs it in your desired pattern. The spread operator (...item.json) keeps all original fields while adding the new one.

Common use case: Preparing dates for database inserts or API calls that require a specific format.

Recipe 2: Get Current Timestamp in Multiple Formats

Problem: You need the current date and time in various formats for logging or record-keeping.

const now = DateTime.now();

return [{
  json: {
    iso: now.toISO(),
    date: now.toFormat('yyyy-MM-dd'),
    time: now.toFormat('HH:mm:ss'),
    readable: now.toFormat('MMMM d, yyyy'),
    unix: now.toSeconds(),
    unixMillis: now.toMillis()
  }
}];

How it works: DateTime.now() captures the current moment. Each toFormat() call shapes it differently. toSeconds() and toMillis() give Unix timestamps for systems that expect them.

Recipe 3: Calculate Days Between Two Dates

Problem: You need to know how many days passed between two events.

const items = $input.all();

return items.map(item => {
  const startDate = DateTime.fromISO(item.json.startDate);
  const endDate = DateTime.fromISO(item.json.endDate);
  const diff = endDate.diff(startDate, 'days');

  return {
    json: {
      ...item.json,
      daysBetween: Math.round(diff.days)
    }
  };
});

How it works: Luxon’s diff() method calculates the difference between two DateTime objects. Specify the unit you want (‘days’, ‘hours’, ‘months’). Math.round() handles fractional days.

Common use case: SLA tracking, subscription period calculations, or aging reports.

Recipe 4: Add or Subtract Days from a Date

Problem: You need to calculate a due date or expiration date.

const items = $input.all();

return items.map(item => {
  const baseDate = DateTime.fromISO(item.json.orderDate);

  return {
    json: {
      ...item.json,
      dueIn7Days: baseDate.plus({ days: 7 }).toFormat('yyyy-MM-dd'),
      dueIn30Days: baseDate.plus({ days: 30 }).toFormat('yyyy-MM-dd'),
      oneWeekAgo: baseDate.minus({ days: 7 }).toFormat('yyyy-MM-dd')
    }
  };
});

How it works: The plus() and minus() methods accept an object specifying the duration. You can use days, weeks, months, years, hours, and minutes.

Recipe 5: Convert Unix Timestamp to Readable Date

Problem: An API returns timestamps as Unix seconds or milliseconds.

const items = $input.all();

return items.map(item => {
  // For Unix seconds (10 digits like 1702550400)
  const fromSeconds = DateTime.fromSeconds(item.json.timestamp);

  // For Unix milliseconds (13 digits like 1702550400000)
  // const fromMillis = DateTime.fromMillis(item.json.timestampMs);

  return {
    json: {
      ...item.json,
      readableDate: fromSeconds.toFormat('yyyy-MM-dd HH:mm:ss'),
      isoDate: fromSeconds.toISO()
    }
  };
});

How it works: Check your timestamp length. 10 digits means seconds, 13 digits means milliseconds. Use the matching Luxon method.

String Manipulation Recipes

Text data rarely arrives in the format you need. These recipes handle the cleaning and transformation.

Recipe 6: Extract Email Domain

Problem: You have email addresses and need just the domain part for analytics.

const items = $input.all();

return items.map(item => {
  const email = item.json.email || '';
  const domain = email.split('@')[1] || '';

  return {
    json: {
      ...item.json,
      emailDomain: domain.toLowerCase()
    }
  };
});

How it works: split('@') breaks the email into parts. Index [1] grabs everything after the @ symbol. The || '' fallback prevents errors if the email is missing.

Recipe 7: Clean and Normalize Text

Problem: User input contains extra whitespace, inconsistent casing, or special characters.

const items = $input.all();

return items.map(item => {
  const rawText = item.json.userInput || '';

  const cleaned = rawText
    .trim()                           // Remove leading/trailing spaces
    .replace(/\s+/g, ' ')             // Collapse multiple spaces to one
    .toLowerCase();                    // Normalize to lowercase

  return {
    json: {
      ...item.json,
      cleanedText: cleaned
    }
  };
});

How it works: Method chaining applies multiple transformations. The regex /\s+/g matches any whitespace character, one or more times, globally.

Recipe 8: Convert to Title Case

Problem: You need “john smith” to become “John Smith”.

const items = $input.all();

function toTitleCase(str) {
  return str
    .toLowerCase()
    .split(' ')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join(' ');
}

return items.map(item => {
  return {
    json: {
      ...item.json,
      titleName: toTitleCase(item.json.name || '')
    }
  };
});

How it works: Split by spaces, capitalize the first character of each word, rejoin with spaces. The helper function keeps the main logic clean.

Recipe 9: Generate URL Slug from Text

Problem: You need to create URL-friendly slugs from titles like “My Blog Post!” to “my-blog-post”.

const items = $input.all();

function slugify(text) {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '')     // Remove special characters
    .replace(/\s+/g, '-')          // Replace spaces with hyphens
    .replace(/-+/g, '-');          // Collapse multiple hyphens
}

return items.map(item => {
  return {
    json: {
      ...item.json,
      slug: slugify(item.json.title || '')
    }
  };
});

How it works: This sequential replacement pattern handles most edge cases. The regex [^\w\s-] matches anything that isn’t a word character, whitespace, or hyphen.

Recipe 10: Extract Numbers from a String

Problem: Text contains numbers you need to extract, like “Order #12345” or “Total: $99.50”.

const items = $input.all();

return items.map(item => {
  const text = item.json.text || '';

  // Extract all numbers (including decimals)
  const numbers = text.match(/[\d.]+/g) || [];

  // Get just the first number as a float
  const firstNumber = numbers.length > 0 ? parseFloat(numbers[0]) : null;

  return {
    json: {
      ...item.json,
      extractedNumbers: numbers,
      firstNumber: firstNumber
    }
  };
});

How it works: The regex [\d.]+ matches digits and decimal points. match() returns all matches as an array. parseFloat() converts string to number.

Array Operations Recipes

When your data comes as lists, you need to filter, sort, and aggregate. These are the most requested Code node operations.

Recipe 11: Filter Items by Condition

Problem: You have a list of orders and only want the completed ones.

const items = $input.all();

// Get the array from your data
const orders = items[0].json.orders || [];

// Filter by condition
const completedOrders = orders.filter(order => order.status === 'completed');

return [{
  json: {
    completedOrders: completedOrders,
    totalCompleted: completedOrders.length
  }
}];

How it works: The filter() method creates a new array containing only elements where the callback returns true. This doesn’t modify the original array.

Multiple conditions:

const filtered = orders.filter(order =>
  order.status === 'completed' && order.total > 100
);

Recipe 12: Remove Duplicates from Array

Problem: Your data has duplicate entries you need to eliminate.

const items = $input.all();
const emails = items[0].json.emails || [];

// Simple array of strings
const uniqueEmails = [...new Set(emails)];

return [{
  json: {
    uniqueEmails: uniqueEmails
  }
}];

For objects, remove duplicates by a specific key:

const items = $input.all();
const users = items[0].json.users || [];

// Remove duplicate users by email
const seen = new Set();
const uniqueUsers = users.filter(user => {
  if (seen.has(user.email)) {
    return false;
  }
  seen.add(user.email);
  return true;
});

return [{
  json: {
    uniqueUsers: uniqueUsers
  }
}];

How it works: Set automatically removes duplicates for primitive values. For objects, we track seen values manually.

Recipe 13: Sort Array by Property

Problem: You need items ordered by date, price, or name.

const items = $input.all();
const products = items[0].json.products || [];

// Sort by price (ascending)
const byPriceAsc = [...products].sort((a, b) => a.price - b.price);

// Sort by price (descending)
const byPriceDesc = [...products].sort((a, b) => b.price - a.price);

// Sort by name (alphabetical)
const byName = [...products].sort((a, b) => a.name.localeCompare(b.name));

// Sort by date (newest first)
const byDate = [...products].sort((a, b) =>
  new Date(b.createdAt) - new Date(a.createdAt)
);

return [{
  json: {
    byPriceAsc,
    byPriceDesc,
    byName,
    byDate
  }
}];

How it works: The sort() callback receives two elements. Return negative to place a first, positive for b first, zero for equal. The spread operator [...products] creates a copy to avoid mutating the original.

Recipe 14: Find Specific Item in Array

Problem: You need to locate one item by ID or other identifier.

const items = $input.all();
const users = items[0].json.users || [];
const targetId = items[0].json.targetUserId;

// Find single item
const foundUser = users.find(user => user.id === targetId);

// Check if item exists
const userExists = users.some(user => user.id === targetId);

// Find index of item
const userIndex = users.findIndex(user => user.id === targetId);

return [{
  json: {
    foundUser: foundUser || null,
    userExists: userExists,
    userIndex: userIndex
  }
}];

How it works: find() returns the first match or undefined. some() returns a boolean. findIndex() returns the position or -1 if not found.

Recipe 15: Sum Values in Array

Problem: You need totals, averages, or other aggregations.

const items = $input.all();
const orders = items[0].json.orders || [];

// Sum all order totals
const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0);

// Count orders by status
const statusCounts = orders.reduce((counts, order) => {
  counts[order.status] = (counts[order.status] || 0) + 1;
  return counts;
}, {});

// Calculate average
const averageOrder = orders.length > 0 ? totalRevenue / orders.length : 0;

return [{
  json: {
    totalRevenue: totalRevenue.toFixed(2),
    averageOrder: averageOrder.toFixed(2),
    orderCount: orders.length,
    statusCounts: statusCounts
  }
}];

How it works: reduce() iterates through the array, accumulating a result. The second argument (0 or {}) sets the initial value.

Recipe 16: Group Items by Category

Problem: You have a flat list and need it organized by a property.

const items = $input.all();
const products = items[0].json.products || [];

const grouped = products.reduce((groups, product) => {
  const category = product.category || 'Uncategorized';

  if (!groups[category]) {
    groups[category] = [];
  }

  groups[category].push(product);
  return groups;
}, {});

return [{
  json: {
    groupedProducts: grouped,
    categories: Object.keys(grouped)
  }
}];

How it works: We build an object where each key is a category and each value is an array of items in that category. This is a fundamental data reshaping pattern.

Recipe 17: Flatten Nested Arrays

Problem: You have arrays inside arrays and need everything at one level.

const items = $input.all();
const nestedData = items[0].json.data || [];

// Flatten one level deep
const flattenedOnce = nestedData.flat();

// Flatten completely (all levels)
const flattenedAll = nestedData.flat(Infinity);

// Flatten and map in one step
const orderItems = items[0].json.orders || [];
const allLineItems = orderItems.flatMap(order => order.lineItems || []);

return [{
  json: {
    flattenedOnce,
    flattenedAll,
    allLineItems
  }
}];

How it works: flat() removes one level of nesting by default. Pass a number for specific depth or Infinity for complete flattening. flatMap() combines mapping and flattening.

JSON Data Transformation Recipes

Real-world data rarely matches the structure your destination expects. These recipes reshape JSON objects.

Recipe 18: Merge Multiple Items into One

Problem: You have separate items that should become a single combined record.

const items = $input.all();

// Merge all items into one object
const merged = items.reduce((result, item) => {
  return { ...result, ...item.json };
}, {});

return [{
  json: merged
}];

For arrays from multiple items:

const items = $input.all();

// Combine arrays from all items
const allRecords = items.flatMap(item => item.json.records || []);

return [{
  json: {
    combinedRecords: allRecords,
    totalCount: allRecords.length
  }
}];

How it works: The spread operator merges objects. Later properties override earlier ones with the same key. For arrays, flatMap() combines and flattens.

Recipe 19: Rename Object Keys

Problem: API field names don’t match what your destination system expects.

const items = $input.all();

const keyMapping = {
  'first_name': 'firstName',
  'last_name': 'lastName',
  'email_address': 'email',
  'phone_number': 'phone'
};

return items.map(item => {
  const transformed = {};

  for (const [oldKey, newKey] of Object.entries(keyMapping)) {
    if (item.json[oldKey] !== undefined) {
      transformed[newKey] = item.json[oldKey];
    }
  }

  return {
    json: transformed
  };
});

How it works: Define a mapping object with old keys and new keys. Loop through the mapping to build the new object. This approach makes the mapping easy to modify.

Recipe 20: Pick Specific Fields Only

Problem: You have objects with 50 fields but only need 5.

const items = $input.all();

const fieldsToKeep = ['id', 'name', 'email', 'status', 'createdAt'];

return items.map(item => {
  const picked = {};

  for (const field of fieldsToKeep) {
    if (item.json[field] !== undefined) {
      picked[field] = item.json[field];
    }
  }

  return {
    json: picked
  };
});

Alternative using destructuring (when you know the exact fields):

const items = $input.all();

return items.map(item => {
  const { id, name, email, status, createdAt } = item.json;

  return {
    json: { id, name, email, status, createdAt }
  };
});

How it works: Both approaches create a new object with only the specified fields. The destructuring approach is cleaner when fields are known and stable.

Recipe 21: Handle Null and Undefined Values Safely

Problem: Missing data causes errors or passes unwanted nulls to your destination.

const items = $input.all();

return items.map(item => {
  const data = item.json;

  return {
    json: {
      // Provide defaults for missing values
      name: data.name || 'Unknown',
      email: data.email || null,

      // Safely access nested properties
      city: data.address?.city || 'Not specified',

      // Convert empty strings to null
      phone: data.phone?.trim() || null,

      // Ensure number type
      age: typeof data.age === 'number' ? data.age : null,

      // Handle arrays that might be null
      tags: Array.isArray(data.tags) ? data.tags : []
    }
  };
});

How it works: Optional chaining (?.) prevents errors on null objects. The || operator provides fallbacks. Type checks ensure data integrity.

Recipe 22: Parse JSON String to Object

Problem: A field contains JSON as a string that needs to become an actual object.

const items = $input.all();

return items.map(item => {
  let parsedData = null;

  try {
    // Handle JSON stored as string
    if (typeof item.json.metadata === 'string') {
      parsedData = JSON.parse(item.json.metadata);
    } else {
      parsedData = item.json.metadata;
    }
  } catch (error) {
    console.log('JSON parse error:', error.message);
    parsedData = null;
  }

  return {
    json: {
      ...item.json,
      parsedMetadata: parsedData
    }
  };
});

How it works: JSON.parse() converts a JSON string to an object. The try-catch prevents the entire workflow from failing if one item has malformed JSON. Always wrap JSON.parse() in error handling.

API Response Processing Recipes

APIs return data in their own formats. These recipes handle common real-world response structures.

Recipe 23: Extract Nested Data from API Response

Problem: The data you need is buried deep inside the API response.

const items = $input.all();
const response = items[0].json;

// Common patterns for nested API responses
const data = response.data
  || response.results
  || response.items
  || response.records
  || [];

// Handle pagination metadata
const pagination = {
  total: response.total || response.count || data.length,
  page: response.page || response.current_page || 1,
  hasMore: response.has_more || response.next_page !== null
};

// Convert each record to n8n item format
return data.map(record => ({
  json: {
    ...record,
    _pagination: pagination
  }
}));

How it works: Different APIs use different wrapper structures. The fallback chain (||) handles multiple conventions. Converting to individual items makes downstream processing easier.

Recipe 24: Transform API Response Structure

Problem: The API response structure doesn’t match what you need to send to another system.

const items = $input.all();
const apiResponse = items[0].json;

// Transform from one API format to another
const transformed = {
  contacts: (apiResponse.users || []).map(user => ({
    externalId: user.id.toString(),
    fullName: `${user.first_name} ${user.last_name}`.trim(),
    emailAddress: user.email?.toLowerCase(),
    phoneNumbers: [
      user.phone && { type: 'mobile', number: user.phone },
      user.work_phone && { type: 'work', number: user.work_phone }
    ].filter(Boolean),
    customFields: {
      source: 'api_import',
      importedAt: DateTime.now().toISO(),
      originalId: user.id
    }
  })),
  metadata: {
    importDate: DateTime.now().toISO(),
    recordCount: (apiResponse.users || []).length
  }
};

return [{
  json: transformed
}];

How it works: This recipe combines multiple techniques: field renaming, value transformation, conditional field inclusion, and metadata addition. The .filter(Boolean) removes any falsy values from the phone numbers array.

Recipe 25: Batch Process Items with Rate Limiting

Problem: You need to process items in chunks or add delays for rate-limited APIs.

const items = $input.all();
const allRecords = items[0].json.records || [];

const BATCH_SIZE = 100;

// Split into batches
const batches = [];
for (let i = 0; i < allRecords.length; i += BATCH_SIZE) {
  batches.push(allRecords.slice(i, i + BATCH_SIZE));
}

// Return batches as separate items for downstream processing
return batches.map((batch, index) => ({
  json: {
    batchNumber: index + 1,
    totalBatches: batches.length,
    records: batch,
    recordCount: batch.length
  }
}));

How it works: This splits a large array into smaller chunks. Each batch becomes a separate item, which you can then process with subsequent nodes. Combine this with a Split In Batches node for controlled processing.

For handling API timeouts and errors, see our timeout troubleshooting guide.

Error Handling Patterns

Production workflows need to handle failures gracefully. These patterns prevent cascading errors.

Try-Catch for Safe Execution

const items = $input.all();

return items.map(item => {
  try {
    // Your transformation logic here
    const result = someRiskyOperation(item.json);

    return {
      json: {
        success: true,
        data: result
      }
    };
  } catch (error) {
    console.log('Processing error:', error.message);

    return {
      json: {
        success: false,
        error: error.message,
        originalData: item.json
      }
    };
  }
});

Safe Data Access Helper

function safeGet(obj, path, defaultValue = null) {
  return path.split('.').reduce((current, key) => {
    return current && current[key] !== undefined ? current[key] : defaultValue;
  }, obj);
}

// Usage
const items = $input.all();

return items.map(item => ({
  json: {
    name: safeGet(item.json, 'user.profile.name', 'Unknown'),
    email: safeGet(item.json, 'user.contact.email', ''),
    city: safeGet(item.json, 'user.address.city', 'Not provided')
  }
}));

Return Empty Array on Failure

When the entire Code node should fail gracefully:

const items = $input.all();

try {
  // Complex processing that might fail
  const processed = complexTransformation(items);
  return processed;
} catch (error) {
  console.log('Code node failed:', error.message);

  // Return empty array to continue workflow
  return [];
}

Common Pitfalls Reference

PitfallSymptomFix
Wrong return structure”Error: Cannot read property ‘json‘“Return [{ json: {...} }] format
Undefined access”Cannot read property ‘x’ of undefined”Use optional chaining ?.
Forgetting awaitPromise object in output instead of dataThis Code node is synchronous; use HTTP Request node for async calls
Mutating inputUnexpected changes in other nodesCreate copies with spread operator or JSON.parse(JSON.stringify())

When NOT to Use the Code Node

The n8n community actively debates overuse of Code nodes. Here’s when native nodes are better.

TaskCode NodeBetter Alternative
Filter items by one conditionPossible but overkillFilter node or IF node
Format a single datePossible but overkillExpression with toFormat()
Basic math on fieldsPossible but overkillExpression with operators
Sort items by one fieldPossible but overkillSort node
Merge two data streamsPossible but complexMerge node
Simple field mappingPossible but overkillSet node with expressions
Split into branchesNot the right toolSwitch node or IF node

Use Code nodes when:

  • The transformation requires multiple steps on multiple fields
  • You’re aggregating data (sum, count, group by)
  • Visual nodes would require 5+ nodes chained together
  • Performance matters for large datasets

For expression-based transformations, see our complete n8n expressions cheatsheet.

Frequently Asked Questions

Why does my Code node return undefined?

This happens when you forget to return a value or return the wrong structure. Every Code node must end with a return statement producing an array of items.

Common causes:

  1. No return statement at the end
  2. Returning raw data instead of [{ json: {...} }] format
  3. A function inside the code doesn’t return (arrow functions without braces return implicitly; with braces, you need explicit return)

Fix: Add console.log() before your return to verify the data exists, then ensure you wrap it in the correct structure.

How do I access data from a previous node in the Code node?

Use the $() function with the exact node name:

// Get all items from a node called "HTTP Request"
const httpData = $('HTTP Request').all();

// Get first item's JSON
const firstItem = $('HTTP Request').first().json;

// Get specific field
const userId = $('HTTP Request').first().json.userId;

The node name must match exactly, including spaces and capitalization. You can find the exact name in the node’s header bar.

What’s the difference between Run Once for All Items vs Each Item?

Run Once for All Items: Your code executes one time. You receive all incoming items as an array via $input.all(). Use this for aggregations, batch processing, or when items need to interact with each other.

Run Once for Each Item: Your code executes separately for each item. Access the current item directly via $json. Use this for simple per-item transformations where items are independent.

Rule of thumb: Start with “All Items” for more control. Switch to “Each Item” only for simple, independent transformations.

Can I use npm packages in the Code node?

If you self-host n8n, you can enable external npm modules by setting the NODE_FUNCTION_ALLOW_EXTERNAL environment variable. n8n Cloud has limitations on external packages.

# In your n8n environment
NODE_FUNCTION_ALLOW_EXTERNAL=lodash,axios

Then in your Code node:

const _ = require('lodash');
const grouped = _.groupBy(items, 'category');

For most transformations, built-in JavaScript methods and Luxon (included by default) are sufficient. See the n8n documentation for details on enabling external modules.

How do I debug Code node errors?

Step 1: Add console.log() statements to trace data flow:

const items = $input.all();
console.log('Number of items:', items.length);
console.log('First item:', JSON.stringify(items[0], null, 2));

Step 2: Check the browser console (F12) for your log output.

Step 3: Test with simplified data. If your Code node works with simple test data but fails with real data, the issue is data-related, not code-related.

Step 4: Use try-catch to capture specific errors:

try {
  // Your code here
} catch (error) {
  console.log('Error details:', error.message, error.stack);
  return [{ json: { error: error.message } }];
}

For complex debugging scenarios, use our free n8n workflow debugger.

Next Steps

You now have 25 production-ready recipes covering the most common Code node scenarios. Bookmark this page and return when you need a specific transformation.

Continue learning:

If your workflows need professional architecture or you’re stuck on a complex transformation, our n8n consulting services can help you build maintainable, scalable automations.

Ready to Automate Your Business?

Tell us what you need automated. We'll build it, test it, and deploy it fast.

✓ 48-72 Hour Turnaround
✓ Production Ready
✓ Free Consultation
⚡

Create Your Free Account

Sign up once, use all tools free forever. We require accounts to prevent abuse and keep our tools running for everyone.

or

You're in!

Check your email for next steps.

By signing up, you agree to our Terms of Service and Privacy Policy. No spam, unsubscribe anytime.

🚀

Get Expert Help

Add your email and one of our n8n experts will reach out to help with your automation needs.

or

We'll be in touch!

One of our experts will reach out soon.

By submitting, you agree to our Terms of Service and Privacy Policy. No spam, unsubscribe anytime.