Skip to main content

Loop Iteration

The loop: step attribute enables iterating over collections with configurable execution modes. State is managed via NATS KV snapshots.

Important: loop: is a step-level attribute, NOT a tool kind. It modifies how a step executes.

Basic Usage

- step: process_items
tool:
kind: python
libs: {}
args:
item: "{{ current_item }}"
code: |
result = {"processed_id": item["id"]}
loop:
in: "{{ workload.items }}"
iterator: current_item
mode: sequential
next:
- step: end

Configuration

FieldTypeRequiredDescription
instring/arrayYesJinja2 expression or array to iterate over
iteratorstringYesVariable name for current item
modestringNosequential (default) or parallel

Execution Modes

Sequential Mode

Items processed one at a time, in order:

- step: fetch_weather
tool:
kind: http
method: GET
url: "https://api.weather.com/city/{{ city.name }}"
params:
lat: "{{ city.lat }}"
lon: "{{ city.lon }}"
loop:
in: "{{ workload.cities }}"
iterator: city
mode: sequential
next:
- step: end

Parallel Mode

Items processed concurrently (limited by worker pool):

- step: fetch_data
tool:
kind: http
method: GET
url: "{{ url }}"
loop:
in: "{{ workload.urls }}"
iterator: url
mode: parallel
next:
- step: end

Accessing Iterator Variables

Within the loop body, access the current element directly by the iterator name:

- step: fetch_weather
tool:
kind: http
method: GET
url: "https://api.weather.com/city/{{ city.name }}"
params:
lat: "{{ city.lat }}"
lon: "{{ city.lon }}"
loop:
in: "{{ workload.cities }}"
iterator: city
mode: sequential

Loop with Sink

Save results from each iteration:

- step: process_and_save
tool:
kind: python
libs: {}
args:
record: "{{ current_record }}"
code: |
result = {"processed_id": record["id"], "status": "complete"}
loop:
in: "{{ workload.records }}"
iterator: current_record
mode: parallel
case:
- when: "{{ event.name == 'step.exit' and response is defined }}"
then:
sink:
tool:
kind: postgres
auth: "{{ workload.pg_auth }}"
table: processed_records

Nested Loops

For multi-dimensional processing, use separate steps with loops:

- step: process_categories
tool:
kind: python
libs: {}
args:
category: "{{ cat }}"
code: |
result = {"category_id": category["id"], "items": category["items"]}
loop:
in: "{{ workload.categories }}"
iterator: cat
mode: sequential
vars:
current_items: "{{ result.items }}"
next:
- step: process_items

- step: process_items
tool:
kind: python
libs: {}
args:
item: "{{ current_item }}"
code: |
result = {"item_id": item["id"], "processed": True}
loop:
in: "{{ vars.current_items }}"
iterator: current_item
mode: sequential
next:
- step: end

HTTP Pagination with Loop

Combine HTTP pagination with iteration for complex data fetching:

- step: fetch_all_pages
tool:
kind: http
method: GET
url: "{{ workload.api_url }}"
params:
page: 1
loop:
pagination:
type: response_based
continue_while: "{{ response.data.hasMore }}"
next_page:
params:
page: "{{ (response.data.page | int) + 1 }}"
merge_strategy: append
merge_path: data.items
vars:
all_items: "{{ result.data }}"
next:
- step: process_items

- step: process_items
tool:
kind: python
libs: {}
args:
item: "{{ current_item }}"
code: |
result = {"item_id": item["id"], "transformed": True}
loop:
in: "{{ vars.all_items }}"
iterator: current_item
mode: parallel
next:
- step: end

Working Examples

See complete loop playbooks: