Introduction

Before you wire up your first real workflow in n8n, you need to understand one thing that trips up almost every beginner: n8n does not pass raw files or streams between nodes. It passes items. Everything — a webhook payload, a database row, an API response — gets wrapped into a structured item before it moves to the next node. Get this mental model right early, and debugging becomes dramatically easier.

What Is an Item?

An item is the fundamental unit of data in n8n. Every node receives an array of items and outputs an array of items. Each item is a JavaScript object with a required key called json, which holds your actual payload data as a JSON object. Optionally, an item can also carry binary data (for files, images, etc.), but that is covered separately.

A single item looks like this at its core:

json
{
"json": {
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
}

When a node like HTTP Request fetches a list of users from an API, n8n does not hand the next node one big array. It splits that array and gives the next node one item per user. This means if your API returns 10 users, the downstream node runs 10 times — once per item. That behavior is fundamental to how n8n thinks.

Flowchart showing HTTP Request node receiving a JSON array of 3 users, then splitting into 3 separate items flowing into the next node

How JSON Flows Between Nodes

When you reference data from a previous node, you are always reaching into the json key of an item. In expressions, n8n uses the syntax below to access the output of the previous node:

javascript
{{ $json.fieldName }}

If you want to grab data from a specific named node rather than the immediately previous one, you use:

javascript
{{ $node["NodeName"].json.fieldName }}

In practice, if you have a Set node that produces an item with the structure shown earlier, the next node can reference the user's email like this:

javascript
{{ $json.email }}

The key thing to internalize: you are never working with the raw HTTP response body as a string. By the time data reaches your expression, n8n has already parsed it into the json object. You do not need to call JSON.parse() yourself.

The Execution Model

n8n processes items one at a time through each node, in sequence. When a node receives multiple items, it iterates over them automatically — you do not write a loop. This is called the item-based execution model.

Consider a workflow that reads 50 rows from a database, transforms each row, and sends an email per row. You do not write "for each row, send email." You just connect the nodes. n8n handles the iteration.

This has important implications:

First, every node in the chain runs once per item by default. A node that appends a timestamp field will run 50 times for 50 items, producing 50 output items.

Second, some nodes deliberately merge or split the item count. The Merge node can combine streams. The SplitInBatches node can group items. The Code node can reshape the entire array if needed. Being aware of how your item count changes across the workflow is key to avoiding unexpected behavior.

Third, if any node fails on one item, by default the whole execution stops. You can configure error handling per-node to change this, but the default is fail-fast.

Block diagram showing the execution model: Node A outputs 4 items, Node B processes each item individually producing 4 outputs, Node C (Merge) combines them back into 1 item

Practical Example

Here is what an item array actually looks like when it enters a Code node. You can inspect this yourself using the built-in debugger by clicking any node's output panel.

json
[
{
"json": {
"orderId": "ORD-001",
"amount": 120.5,
"status": "pending"
}
},
{
"json": {
"orderId": "ORD-002",
"amount": 89.0,
"status": "shipped"
}
}
]

If you place a Code node after this and want to add a processed flag to each item, you write:

javascript
for (const item of items) {
item.json.processed = true;
}
return items;

Notice you iterate over items yourself inside the Code node — that is the one exception to the "n8n loops for you" rule. The Code node hands you the full array and expects the full array back.

Summary

Every piece of data in n8n is wrapped in an item with a json key. Nodes receive and emit arrays of items, and by default each node runs once per item — you do not manage loops outside the Code node. Expressions like $json.fieldName are how you reach into item data. Understanding this execution model is the foundation for everything else: transformations, branching, merging, and error handling all build directly on top of it.

Lesson Checkpoint

1. What is the fundamental unit of data passed between nodes in n8n?

2. When an HTTP Request node fetches an API response that contains an array of 10 users, how does n8n pass this data to the next node by default?

3. Which expression syntax correctly accesses the email field from the current item in the previous node?

4. In n8n's item-based execution model, if a node receives 50 items, how many times does that node run by default?

5. Inside a Code node, how do you correctly add a new field to every item and return the result?

6. What happens by default in n8n when a node fails while processing one of the items in a workflow?

7. How do you reference a specific field from a named upstream node (not the immediately previous one) in an n8n expression?