Skip to main content
Create your own
Lesson illustration

Nested Loops for Complex Data

Hello! Welcome back to our series on n8n.

In the last lesson, we focused on the Merge node, a powerful tool for combining data from different branches. We saw how to use it to both stack data (Append) and enrich it (Combine), which are fundamental skills for managing parallel data streams.

Today, we're diving deeper into advanced flow control to tackle a common challenge in programming and automation: nested loops. This lesson will guide you to achieve the learning outcome: Implement nested loops for complex data structures (e.g., using a sub-workflow).

As a software developer, you're very familiar with writing nested for loops to iterate over hierarchical data, like processing all orders for each customer. In n8n's visual, item-based paradigm, this requires a specific and powerful design pattern: the sub-workflow. We will explore why this pattern is the recommended approach for creating clean, scalable, and reusable logic.

1. The Challenge of Nested Loops in n8n

n8n processes data as a series of items flowing from one node to the next. A standard loop is achieved by having a node process each incoming item individually. For example, if you pass 10 items to an HTTP Request node, it will execute 10 times, once for each item.

But what if each of those 10 items itself contains a list that you need to iterate over? This is a nested loop. A simple, single-workflow loop structure can't handle this elegantly. Trying to build nested loops within one workflow can become confusing and hard to maintain.

The solution is to embrace modularity, a concept you'll recognize from software engineering. We break the problem down into two parts:

  • An outer loop in a parent workflow to handle the primary items (e.g., customers).
  • An inner loop in a sub-workflow to handle the nested items (e.g., orders).

2. Understanding Sub-workflows: The Key to Modularity

A sub-workflow is a standalone workflow that can be called and executed by another workflow. This allows you to create reusable modules for common tasks, which is a cornerstone of building robust and maintainable automations.

The key nodes that enable this are:

  • Execute Workflow node: Used in the parent workflow to call the sub-workflow. It passes its own input items to the sub-workflow.
  • Execute Workflow Trigger node: Used in the sub-workflow to receive the data from the parent.

To get a clear picture of how sub-workflows function and why they're beneficial, let's watch a short video from the n8n team.

n8n Advanced Course (4/8) - Subworkflows

This video, 'n8n Advanced Course (4/8) - Subworkflows', provides an excellent conceptual overview of sub-workflows, the data flow between parent and child workflows, and the benefits of this modular approach.

Watch from the beginning to 02:40. Focus on understanding the data transfer mechanism—how data flows from the parent, is processed by the sub-workflow, and how the results are returned.

As the video explains, this pattern is not just for loops; it's a fundamental technique for abstracting complex tasks and promoting reusability. The "Nested Processing" pattern is a specific application of this concept.

n8n Workflow Design Patterns: Error Handling & Production Setup

This blog post on workflow design patterns provides excellent context on why modular design is crucial. It directly describes the 'Nested Processing' pattern we're implementing today.

Read the section titled 'Pattern 3: Nested Processing'. The 'Order Processing' example with line items is exactly what we're about to build. This will reinforce the real-world applicability of this pattern.

3. Hands-On: Building a Nested Loop

Let's build a practical example: for a list of customers, we'll process a list of their recent orders. The outer loop will iterate through customers, and the inner loop (in the sub-workflow) will iterate through each customer's orders.

We'll follow the recommended approach outlined in a helpful community tutorial.

N8n Nested Loop Tutorial - Complete End-to-End Guide

This 'N8n Nested Loop Tutorial' from the community forum is our primary guide. We will implement 'Solution 1: Using Sub-Workflow'. While the tutorial uses some older node patterns, the core logic and code snippets are perfect for our exercise.

Read the 'Scenario' and the 'MAIN WORKFLOW' and 'SUB-WORKFLOW' sections under 'Solution 1'. We will adapt this structure to the modern n8n interface. You can copy the JavaScript code snippets from the tutorial directly into your Code nodes.

Step 1: Create the Sub-Workflow (Inner Loop)

First, let's build the workflow that will process the orders for a single customer.

  1. Create a new workflow. Name it something like "Sub - Process Orders".
  2. Add an Execute Workflow Trigger node. This is the entry point. It will receive one customer item at a time from the parent workflow.
  3. Add a Code node and name it "Create Orders". Connect it to the trigger. This node will simulate fetching the orders for the customer we received. Paste the following JavaScript code from the tutorial into it:
    const orders = [
      { orderId: "ORD-001", product: "Laptop", amount: 999 },
      { orderId: "ORD-002", product: "Mouse", amount: 25 }
    ];
    
    return orders.map(order => ({ json: order }));
    
    This code generates two sample orders. It will output two items.
  4. Add another Code node and name it "Process Order". Connect it to the "Create Orders" node. This node represents our inner loop's processing logic. For each order item it receives, it will merge the customer data from the trigger. Paste this code:
    // Get the customer data passed from the parent workflow
    const customerData = $('Execute Workflow Trigger').first().json;
    // Get the current order item being processed
    const orderData = $input.item.json;
    
    console.log(`Processing Order ${orderData.orderId} for Customer ${customerData.name}`);
    
    // Combine customer and order data and return it
    return {
      json: {
        customerName: customerData.name,
        customerEmail: customerData.email,
        orderId: orderData.orderId,
        product: orderData.product,
        amount: orderData.amount,
        processedAt: new Date().toISOString()
      }
    };
    
  5. Save and activate this sub-workflow. Copy the workflow ID from the URL. It will look something like .../workflow/31. You will need this ID for the parent workflow.

Your sub-workflow is now ready. It accepts a customer, generates two orders, and for each order, it combines the customer and order details into a final processed item.

Step 2: Create the Main Workflow (Outer Loop)

Now, let's create the parent workflow that iterates through the customers.

  1. Create a new workflow. Name it something like "Main - Customer Orders".
  2. Add a Manual trigger (or use the default Start node).
  3. Add a Code node named "Create Customers". This will generate our list of customers. Paste in this code from the tutorial:
    const customers = [
      { id: 1, name: "John Doe", email: "john@example.com" },
      { id: 2, name: "Jane Smith", email: "jane@example.com" },
      { id: 3, name: "Bob Johnson", email: "bob@example.com" }
    ];
    
    return customers.map(customer => ({ json: customer }));
    
    This node will output three items.
  4. Add an Execute Workflow node. Connect it to the "Create Customers" node.
    • In the Workflow ID field, paste the ID of the sub-workflow you saved earlier.
    • The Execute Workflow node will now run three times, once for each customer item it receives from the "Create Customers" node. This is the outer loop.
    • For each execution, it will trigger the sub-workflow, which in turn runs its own logic (the inner loop over the orders).
  5. Execute the workflow! When you test the main workflow, the Execute Workflow node will run and its output will be the combined results from all the sub-workflow executions. You should see 6 items in the output (3 customers × 2 orders each), with each item containing the combined customer and order data.

You have successfully implemented a nested loop!

Test your understanding!

In the "Process Order" node of our sub-workflow, we used the expression $('Execute Workflow Trigger').first().json to get the customer data. Why is this necessary? Why can't we just access the customer data from the previous "Create Orders" node?

Show answer

The "Create Orders" node doesn't contain any customer data. It simply generates a static list of two order items. The customer data exists only as the input to the sub-workflow, which is available at the Execute Workflow Trigger node. Therefore, to combine the customer data with each order, we must explicitly reference the data from the trigger node within our "inner loop" processing step.

4. Alternative Methods and Best Practices

The sub-workflow pattern is the most robust and scalable method, but it's good to be aware of other techniques.

The community tutorial we used also discusses two other solutions:

  1. Single Workflow with Smart Reset: A clever technique that uses a conditional Reset setting on a loop node to simulate nesting within a single workflow. It's more complex to set up and debug.
  2. Flatten Data: A pre-processing approach where you transform the hierarchical data (customers and orders) into a single flat list of all possible combinations before looping. This is the simplest method but only works if you can generate the flattened list easily, which isn't always possible with dynamic data from APIs.

N8n Nested Loop Tutorial - Complete End-to-End Guide

To understand these alternatives and see a helpful comparison, review the rest of the community tutorial.

Quickly read through 'Solution 2: Single Workflow with Smart Reset' and 'Solution 3: Flatten Data' to get the general idea. Then, carefully study the table under 'Which Solution Should You Use?' and the list of 'Common Mistakes to Avoid'.

Finally, as you build more complex workflows, remember these software engineering principles for good sub-workflow design.

  • Single Responsibility: Each sub-workflow should do one thing well.
  • Clear Input/Output: Design your sub-workflows to expect a specific data structure and to return a predictable one. Use a Note node to document this for other users (or your future self!).
  • Decoupling: The parent workflow shouldn't need to know how the sub-workflow works, only what inputs it needs and what outputs it provides.

Conclusion

Today you've mastered one of n8n's most powerful architectural patterns. By using sub-workflows to implement nested loops, you can process complex, hierarchical data in a way that is modular, easy to understand, and highly reusable.

Key Takeaways:

  • Nested loops in n8n are best implemented using a parent workflow that calls a sub-workflow.
  • The parent workflow provides the outer loop, with the Execute Workflow node running once for each primary item (e.g., customer).
  • The sub-workflow, which starts with an Execute Workflow Trigger, provides the inner loop, processing the nested data (e.g., orders) for the single item it received.
  • This modular pattern is highly recommended for its clarity, maintainability, and scalability over single-workflow alternatives.

In our next lesson, we will continue exploring advanced flow control by learning how to use the Wait node to pause a workflow for a fixed duration. This will introduce you to time-based control and workflows that don't necessarily run from start to finish in one go.

Can't find a good explanation? Sign up and we'll make it for you

Sign up