Scenario
You have a list of 3 customers, and for each customer you need to process 2 orders. This requires nested loops.
Expected Result: Process 6 items total (3 customers × 2 orders each)
Solution 1: Using Sub-Workflow (RECOMMENDED)
MAIN WORKFLOW
Step 1: Create the Main Workflow
Node 1: Manual Trigger
-
Add a “Manual” trigger node
-
This starts your workflow
Node 2: Create Sample Data (Code Node)
-
Add a “Code” node named “Create Customers”
-
Use this code:
const customers = [
{ id: 1, name: "John Doe", email: "[email protected]" },
{ id: 2, name: "Jane Smith", email: "[email protected]" },
{ id: 3, name: "Bob Johnson", email: "[email protected]" }
];
return customers.map(customer => ({ json: customer }));
Node 3: Loop Over Items
-
Add “Loop Over Items” node
-
Name it “Loop Customers”
-
Connect it to “Create Customers”
-
Settings:
- Mode: “Run Once for All Items”
Node 4: Execute Workflow
-
Add “Execute Workflow” node
-
Name it “Process Customer Orders”
-
Settings:
-
Source: “Database”
-
Workflow: Select “Process Orders Sub-Workflow” (we’ll create this next)
-
Workflow Parameters: Leave empty or pass customer data as needed
-
-
Connect from “Loop Customers”
Node 5: Loop Complete
- Connect “Execute Workflow” back to “Loop Customers” (creates the loop)
Node 6: NoOp (Final Step)
-
Add a “NoOp” node named “All Done”
-
Connect from “Loop Customers” when loop completes
SUB-WORKFLOW (Process Orders)
Step 2: Create the Sub-Workflow
Create a new workflow and name it “Process Orders Sub-Workflow”
Node 1: Execute Workflow Trigger
-
Add “Execute Workflow Trigger” node
-
This receives data from the main workflow
Node 2: Create Orders (Code Node)
-
Add “Code” node named “Create Orders”
-
Code:
const orders = [
{ orderId: "ORD-001", product: "Laptop", amount: 999 },
{ orderId: "ORD-002", product: "Mouse", amount: 25 }
];
return orders.map(order => ({ json: order }));
Node 3: Loop Over Items
-
Add “Loop Over Items” node
-
Name it “Loop Orders”
-
Settings:
- Mode: “Run Once for All Items”
Node 4: Process Order (Code Node)
-
Add “Code” node named “Process Order”
-
Code:
const customerData = $('Execute Workflow Trigger').first().json;
const orderData = $input.item.json;
console.log(`Processing Order ${orderData.orderId} for Customer ${customerData.name || 'N/A'}`);
return {
json: {
customer: customerData.name || 'N/A',
orderId: orderData.orderId,
product: orderData.product,
amount: orderData.amount,
processedAt: new Date().toISOString()
}
};
Node 5: Loop Back
- Connect “Process Order” back to “Loop Orders”
Node 6: Return Results
-
Add “Respond to Workflow” node
-
Connect from “Loop Orders” when complete
-
This sends results back to main workflow
Solution 2: Single Workflow with Smart Reset
If you prefer everything in one workflow:
Step 1: Setup Data
Node 1: Manual Trigger
Node 2: Create Customers (Code)
const customers = [
{ id: 1, name: "John", orders: [
{ orderId: "ORD-001", product: "Laptop" },
{ orderId: "ORD-002", product: "Mouse" }
]},
{ id: 2, name: "Jane", orders: [
{ orderId: "ORD-003", product: "Keyboard" },
{ orderId: "ORD-004", product: "Monitor" }
]},
{ id: 3, name: "Bob", orders: [
{ orderId: "ORD-005", product: "Webcam" },
{ orderId: "ORD-006", product: "Headset" }
]}
];
return customers.map(customer => ({ json: customer }));
Step 2: Outer Loop
Node 3: Loop Over Items (Outer)
-
Name: “Loop Customers”
-
Mode: “Run Once for All Items”
Node 4: Extract Orders (Code)
-
Name: “Extract Orders”
-
Code:
const customer = $input.item.json;
const orders = customer.orders || [];
return orders.map(order => ({
json: {
customerId: customer.id,
customerName: customer.name,
...order
}
}));
Step 3: Inner Loop with Smart Reset
Node 5: Loop Over Items (Inner)
-
Name: “Loop Orders”
-
Mode: “Run Once for All Items”
-
IMPORTANT - Reset Configuration:
-
Click on “Add Option” → “Reset”
-
In the Reset field, enter:
{{ $prevNode === "Extract Orders" }}- This only resets when entering from the outer loop, not during inner loop iterations
-
Node 6: Process Order (Code)
- Code:
const orderData = $input.item.json;
console.log(`Processing: Customer ${orderData.customerName}, Order ${orderData.orderId}`);
return {
json: {
...orderData,
processedAt: new Date().toISOString(),
status: "completed"
}
};
Node 7: Connect Loops
-
Connect “Process Order” → back to “Loop Orders” (inner loop)
-
Connect “Loop Orders” (when done) → back to “Loop Customers” (outer loop)
Node 8: Final Node
-
Add “NoOp” named “All Done”
-
Connect from “Loop Customers” when complete
Solution 3: Flatten Data (Simplest)
Step 1: Create and Flatten
Node 1: Manual Trigger
Node 2: Create Flattened Data (Code)
const data = [];
const customers = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" },
{ id: 3, name: "Bob" }
];
const orders = [
{ orderId: "ORD-001", product: "Laptop" },
{ orderId: "ORD-002", product: "Mouse" }
];
// Create all combinations
customers.forEach(customer => {
orders.forEach(order => {
data.push({
customerId: customer.id,
customerName: customer.name,
orderId: order.orderId,
product: order.product
});
});
});
return data.map(item => ({ json: item }));
Step 2: Single Loop
Node 3: Loop Over Items
-
Name: “Process All Combinations”
-
Mode: “Run Once for All Items”
Node 4: Process Item (Code)
const item = $input.item.json;
console.log(`Processing: ${item.customerName} - ${item.orderId}`);
return {
json: {
...item,
processedAt: new Date().toISOString()
}
};
Node 5: Loop Back and Complete
-
Connect “Process Item” back to “Process All Combinations”
-
Add “NoOp” node for completion
Testing Your Workflow
-
Activate the workflow
-
Click “Test workflow” button
-
Click on nodes to see their output
-
Check the execution log - you should see:
-
Solution 1/2: 3 customer iterations, each processing 2 orders = 6 total
-
Solution 3: 6 items processed in one loop
-
Which Solution Should You Use?
| Solution | Best For | Pros | Cons |
|---|---|---|---|
| Sub-Workflow | Complex nested logic, reusable processing | Clean, reliable, easy to maintain | Requires two workflows |
| Smart Reset | Keeping everything in one workflow | All in one place | More complex, harder to debug |
| Flatten Data | Simple scenarios with known data structure | Simplest, fastest | Doesn’t work for dynamic nested data |
Recommendation: Start with Sub-Workflow approach - it’s the most reliable and easiest to understand!
Common Mistakes to Avoid
Don’t use simple “Reset” checkbox without conditions
Don’t expect loops to work like traditional for-loops
Don’t forget to connect loop nodes back to themselves
Do use sub-workflows for complex nested scenarios
Do test with small datasets first
Do check execution logs to verify loop counts