n8n Code Node: Run JavaScript and Python in Workflows

Use n8n's Code node to write custom JS and Python logic inside workflows. Transform data, call APIs, parse responses, and handle edge cases.

Problem: Built-In Nodes Don't Cover Your Logic

n8n has 400+ nodes, but sometimes you need to reshape a payload in a way no node supports, call a library function, or implement business logic that doesn't fit a visual block. That's what the Code node is for.

You'll learn:

  • How to use the Code node in both JavaScript and Python modes
  • How to read input items, transform them, and return new items
  • Patterns for error handling, external HTTP calls, and multi-item processing

Time: 20 min | Difficulty: Intermediate


How the Code Node Works

The Code node runs inside n8n's execution environment. JavaScript runs in a sandboxed Node.js context. Python runs via a subprocess (requires Python 3.10+ on your n8n host).

Each execution receives $input.all() — an array of items from the previous node. You return a new array of items. n8n passes that array to the next node.

Previous Node ──items──▶ Code Node ──transformed items──▶ Next Node

Two execution modes matter:

ModeUse when
Run Once for All ItemsYou want to process the full array together (aggregation, dedup)
Run Once for Each ItemYou want to process each item independently (transform, enrich)

Set this in the node's Settings → Run Once for Each Item toggle.


Solution

Step 1: Add a Code Node to Your Workflow

  1. Click + to add a node
  2. Search for Code
  3. Select language: JavaScript or Python
  4. Set execution mode (default is Run Once for All Items)

For the examples below, start with JavaScript.


Step 2: Read and Return Items (JavaScript)

This is the base pattern. Every Code node follows this structure.

// $input.all() returns an array of { json: {...}, binary: {...} }
const items = $input.all();

// Process and return a new array
return items.map(item => {
  return {
    json: {
      ...item.json,                        // keep existing fields
      fullName: `${item.json.firstName} ${item.json.lastName}`,
      processedAt: new Date().toISOString()
    }
  };
});

Rules n8n enforces:

  • Always return an array of objects
  • Each object must have a json key
  • Returning null or an empty array stops execution for that branch

Step 3: Run Once for Each Item Mode

When you enable Run Once for Each Item, n8n calls your function once per item. Use $input.item instead of $input.all().

// $input.item is the single current item
const item = $input.item;

const price = item.json.price;
const qty   = item.json.quantity;

// Validate before operating — bad data crashes the node otherwise
if (typeof price !== 'number' || typeof qty !== 'number') {
  throw new Error(`Invalid data on item: ${JSON.stringify(item.json)}`);
}

return {
  json: {
    ...item.json,
    total: price * qty,
    discount: qty > 10 ? price * qty * 0.1 : 0
  }
};

Note: in this mode you return a single object, not an array.


Step 4: Make HTTP Requests Inside the Code Node

JavaScript has $http available as a built-in helper. Use it instead of fetch to stay inside n8n's credential and proxy system.

const items = $input.all();
const results = [];

for (const item of items) {
  const userId = item.json.userId;

  // $http.get / .post / .put / .delete — mirrors the HTTP Request node
  const response = await $http.get(`https://api.example.com/users/${userId}`, {
    headers: {
      Authorization: `Bearer ${$env.API_TOKEN}` // use n8n env vars, not hardcoded secrets
    }
  });

  results.push({
    json: {
      ...item.json,
      email: response.data.email,
      plan: response.data.subscription.plan
    }
  });
}

return results;

If it fails:

  • 401 Unauthorized → Check that API_TOKEN is set in Settings → Environment Variables
  • TypeError: $http is not defined → You're on n8n < 1.0. Use require('axios') instead, or upgrade

Step 5: Use Python Mode

Switch the Code node language to Python. The input items arrive as _input.all() (underscore prefix, not dollar sign).

# Python mode — n8n 1.x with Python 3.10+ on host
items = _input.all()
output = []

for item in items:
    data = item["json"]

    # Example: parse a date string and reformat it
    from datetime import datetime
    raw_date = data.get("createdAt", "")

    try:
        parsed = datetime.strptime(raw_date, "%Y-%m-%dT%H:%M:%S.%fZ")
        formatted = parsed.strftime("%B %d, %Y")
    except ValueError:
        formatted = "Unknown date"

    output.append({
        "json": {
            **data,
            "formattedDate": formatted
        }
    })

return output

Python-specific notes:

  • import statements work inside the function — no setup required for stdlib
  • Third-party packages (e.g. pandas, httpx) must be installed on the host: pip install pandas
  • n8n Cloud does not support Python Code nodes — self-hosted only

Step 6: Aggregate Items (Reduce Pattern)

Use Run Once for All Items when you need to combine items into one output.

const items = $input.all();

// Sum a field across all items
const total = items.reduce((sum, item) => sum + (item.json.amount ?? 0), 0);

// Collect unique categories
const categories = [...new Set(items.map(i => i.json.category))];

// Return a single summary item
return [
  {
    json: {
      totalAmount: total,
      itemCount: items.length,
      categories,
      generatedAt: new Date().toISOString()
    }
  }
];

Step 7: Access Data from Earlier Nodes

Use $node["Node Name"].json to pull data from any upstream node by name — not just the immediately previous one.

// Pull data from a specific earlier node
const webhookData  = $node["Webhook"].json;
const dbRow        = $node["Postgres"].json;

const item = $input.item;

return {
  json: {
    userId:   webhookData.userId,
    planName: dbRow.plan_name,
    event:    item.json.eventType
  }
};

This is useful when your Code node is downstream of a Merge node and the data you need came from two different branches.


Error Handling Pattern

Wrap risky operations and surface meaningful errors. n8n will mark the execution as failed and show your message in the log.

const items = $input.all();
const results = [];

for (const item of items) {
  try {
    const parsed = JSON.parse(item.json.rawPayload); // may throw

    if (!parsed.orderId) {
      // Skip malformed items but log them — don't crash the whole run
      console.log(`Skipping item missing orderId: ${item.json.rawPayload}`);
      continue;
    }

    results.push({
      json: {
        orderId:   parsed.orderId,
        amount:    parsed.amount,
        currency:  parsed.currency ?? "USD"
      }
    });

  } catch (err) {
    // Re-throw with context so the n8n log is useful
    throw new Error(`Failed to parse item ${item.json.id}: ${err.message}`);
  }
}

return results;

Verification

Run your workflow with a test payload and open the Output panel on the Code node. You should see:

Items: 3
Item 0: { json: { ... your transformed fields ... } }

To test error handling, pass a malformed item and confirm n8n marks the execution red with your custom error message — not a generic crash.


What You Learned

  • The Code node needs $input.all() (JS) or _input.all() (Python) and must return an array of { json: {} } objects
  • Run Once for Each Item simplifies single-item transforms; Run Once for All Items is for aggregation
  • $http is the right way to make HTTP calls — it respects n8n's credential and proxy config
  • Python mode works only on self-hosted n8n with Python 3.10+ installed
  • $node["Name"].json lets you reach data from any upstream node, not just the previous one

When not to use the Code node: if a built-in node covers 90% of your logic, use it. Code nodes are harder to debug visually and don't show in n8n's node graph as structured data. Save them for logic that genuinely has no node equivalent.

Tested on n8n 1.82, Node.js 20.x, Python 3.12, self-hosted via Docker