Complete "Lesson 5: Merging and Combining Data Streams" first to unlock this lesson.Go to previous lesson

Introduction

This is the capstone lesson of the course. You have learned how to use the Code node, the Set node, the IF node, the Merge node, and how to shape data with expressions. Now you will wire all of those pieces together into a single workflow that does something real: pull data from two sources, clean and filter it, transform it into a target shape, merge the streams, and produce a final output ready for downstream use.

The scenario used throughout this lesson is practical and representative of what you will encounter in production: you have an orders list from one HTTP endpoint and a customers list from another. The goal is to produce an enriched order report that contains only paid orders, attaches the customer name and email, and formats dates for display.

end-to-end workflow diagram showing 6 nodes in sequence: HTTP Request (Orders) and HTTP Request (Customers) in parallel, feeding into separate Filter/Transform nodes, then a Merge node, then a Code node for final shaping, then a final output node

Fetching Data from Two Sources in Parallel

Start with two HTTP Request nodes running in parallel. Both are triggered by the same Manual Trigger or Schedule Trigger node. The first fetches orders, the second fetches customers. In n8n, you split execution by connecting one trigger to both HTTP Request nodes.

For the orders endpoint, set the URL and make sure the response format is JSON. The raw response typically looks like this:

json
[
{"id": 101, "customer_id": 5, "status": "paid", "amount": 149.99, "created_at": "2024-03-15T10:22:00Z"},
{"id": 102, "customer_id": 8, "status": "pending", "amount": 89.00, "created_at": "2024-03-16T08:05:00Z"}
]

For the customers endpoint:

json
[
{"id": 5, "name": "Alice Martin", "email": "alice@example.com"},
{"id": 8, "name": "Bob Chen", "email": "bob@example.com"}
]

Both nodes output arrays of items. n8n automatically splits arrays into individual items, so each order and each customer becomes its own item flowing through the rest of the workflow.

Filtering and Transforming Each Stream

Before merging, clean each stream independently. This keeps logic isolated and easier to debug.

For orders, connect an IF node after the orders HTTP Request. Set the condition to check that the field status equals the string paid. Only the true branch continues. This eliminates pending, cancelled, or refunded orders before they ever reach the merge.

Next, connect a Code node after the IF true output to reshape the order item and format the date:

javascript
const items = $input.all();
return items.map(item => {
const d = new Date(item.json.created_at);
const formatted = d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
return {
json: {
order_id: item.json.id,
customer_id: item.json.customer_id,
amount: item.json.amount,
order_date: formatted
}
};
});

For customers, connect a Set node after the customers HTTP Request. Use it to explicitly keep only the fields you need and rename them for clarity. Set customer_key to the expression referencing the id field, keep name and email. This trims noise and makes the merge key unambiguous.

diagram comparing the two parallel transformation branches: Orders branch (IF filter → Code reshape) and Customers branch (Set node cleanup), both feeding into a Merge node

Merging the Two Streams

Connect both transformed streams into a Merge node. Use the Merge by Key mode. Set the key for input 1 (orders) to customer_id and the key for input 2 (customers) to customer_key. n8n will match each order to its customer record and output a combined item containing fields from both.

The merged item at this point contains order_id, customer_id, amount, order_date, customer_key, name, and email all at the top level of item.json. There may be redundant fields like customer_key which you added only for the join. Clean those up in the next step.

Final Shaping and Output

Add one more Code node after the Merge node. This is the final transformation step where you produce the exact output contract you promised to downstream consumers:

javascript
const items = $input.all();
return items.map(item => {
return {
json: {
order_id: item.json.order_id,
order_date: item.json.order_date,
amount_usd: item.json.amount,
customer_name: item.json.name,
customer_email: item.json.email
}
};
});

This node strips internal join keys, renames amount to amount_usd for explicitness, and produces a clean array of enriched order records. Connect this to whatever final node you need: an HTTP Request to POST results to a webhook, a Google Sheets node to append rows, or simply leave it as the terminal node during development and inspect the output in the n8n editor.

Summary

You have built a workflow that covers the full transformation lifecycle: parallel ingestion from two HTTP sources, independent filtering with IF, per-stream reshaping with Code and Set nodes, a key-based merge, and a final cleanup pass. Each node has a single clear responsibility, which makes the workflow readable and debuggable. When something breaks in production, you can pin data at any node and re-run from that point without touching upstream nodes. That composable structure, not any individual node, is the core skill this course has been building toward.

Lesson practice

Lesson Checkpoint

Review what you learned and get feedback on your work.

0/1
Answered · 0/1