# Workflow\.foreach()

The `.foreach()` method creates a loop that executes a step for each item in an array. It always returns an array containing the output from each iteration, preserving the original order.

## Usage example

```typescript
workflow.foreach(step1, { concurrency: 2 })
```

## Parameters

**step** (`Step`): The step instance to execute in the loop. The previous step must return an array type.

**opts** (`object`): Optional configuration for the loop. The concurrency option controls how many iterations can run in parallel (default: 1)

## Returns

**workflow** (`Workflow`): The workflow instance for method chaining. The output type is an array of the step's output type.

## Behavior

### Execution and waiting

The `.foreach()` method processes all items before the next step executes. The step following `.foreach()` only runs after every iteration has completed, regardless of concurrency settings. With `concurrency: 1` (default), items process sequentially. With higher concurrency, items process in parallel batches, but the next step still waits for all batches to finish.

If you need to run multiple operations per item, use a nested workflow as the step. This keeps all operations for each item together and is cleaner than chaining multiple `.foreach()` calls. See [Nested workflows inside foreach](https://mastra.ai/docs/workflows/control-flow) for examples.

### Output structure

`.foreach()` always outputs an array. Each element in the output array corresponds to the result of processing the element at the same index in the input array.

```typescript
// Input: [{ value: 1 }, { value: 2 }, { value: 3 }]
// Step adds 10 to each value
// Output: [{ value: 11 }, { value: 12 }, { value: 13 }]
```

### Using `.then()` after `.foreach()`

When you chain `.then()` after `.foreach()`, the next step receives the entire output array as its input. This allows you to aggregate or process all results together.

```typescript
workflow
  .foreach(processItemStep) // Output: array of processed items
  .then(aggregateStep) // Input: the entire array
  .commit()
```

### Using `.map()` after `.foreach()`

Use `.map()` to transform the array output before passing it to the next step:

```typescript
workflow
  .foreach(processItemStep)
  .map(async ({ inputData }) => ({
    total: inputData.reduce((sum, item) => sum + item.value, 0),
    count: inputData.length,
  }))
  .then(nextStep)
  .commit()
```

### Chaining multiple `.foreach()` calls

When you chain `.foreach()` calls, each operates on the array from the previous step:

```typescript
workflow
  .foreach(stepA) // If input is [a, b, c], output is [A, B, C]
  .foreach(stepB) // Operates on [A, B, C], output is [A', B', C']
  .commit()
```

If a step inside `.foreach()` returns an array, the output becomes an array of arrays. Use `.map()` with `.flat()` to flatten:

```typescript
workflow
  .foreach(chunkStep) // Output: [[chunk1, chunk2], [chunk3, chunk4]]
  .map(async ({ inputData }) => inputData.flat()) // Output: [chunk1, chunk2, chunk3, chunk4]
  .foreach(embedStep)
  .commit()
```

### Progress events during streaming

When using `run.stream()`, foreach steps emit a `workflow-step-progress` event after each iteration completes. This lets you track real-time progress without waiting for the entire foreach to finish.

```typescript
const run = await workflow.createRun()
const stream = run.stream({ inputData })

for await (const chunk of stream) {
  if (chunk.type === 'workflow-step-progress') {
    console.log(`${chunk.payload.completedCount}/${chunk.payload.totalCount}`)
    // e.g. "1/3", "2/3", "3/3"
  }
}
```

Each progress event payload contains:

**id** (`string`): The step ID of the foreach step

**completedCount** (`number`): Number of iterations completed so far

**totalCount** (`number`): Total number of iterations

**currentIndex** (`number`): Index of the iteration that just completed

**iterationStatus** (`'success' | 'failed' | 'suspended'`): Status of the iteration that just completed

**iterationOutput** (`Record<string, any>`): Output of the iteration (present when iterationStatus is 'success')

### Resuming a single iteration

When a step inside `.foreach()` suspends, each iteration suspends independently. Pass `forEachIndex` to [`run.resume()`](https://mastra.ai/reference/workflows/run-methods/resume) to resume one iteration at a time with its own `resumeData`. Omitting `forEachIndex` resumes every suspended iteration with the same data.

```typescript
await run.resume({
  step: 'approve',
  resumeData: { ok: true },
  forEachIndex: 1,
})
```

## Related

- [Looping with foreach](https://mastra.ai/docs/workflows/control-flow)
- [run.resume()](https://mastra.ai/reference/workflows/run-methods/resume)