Skip to main content
Sometimes you don’t know how many steps a workflow needs until it runs. You might need to fetch data for each item in a list, process rows from a query, or fan out across a set of IDs from an API response. OpenWorkflow handles this automatically. When multiple steps share the same name, they’re disambiguated in order (fetch-data, fetch-data:1, fetch-data:2, …). You don’t need to generate unique names yourself.

Basic Pattern

Map over your data and create a step per item using Promise.all:
const results = await Promise.all(
  input.items.map((item) =>
    step.run({ name: "fetch-data" }, async () => {
      return await thirdPartyApi.fetch(item.id);
    }),
  ),
);
Every step uses the same name — OpenWorkflow appends :1, :2, etc. automatically. Each step is individually memoized, so if the workflow restarts, completed steps return their cached results and only the remaining steps re-execute.

Stable IDs for Mutable Collections

If items can be added, removed, or reordered between retries, include a stable ID from the data in the step name instead of relying on auto-indexing:
const results = await Promise.all(
  input.orders.map((order) =>
    step.run({ name: `process-order:${order.id}` }, async () => {
      return await processOrder(order);
    }),
  ),
);
This way, each step is tied to a specific item regardless of its position in the array. Use any stable identifier — a database ID, a slug, or a unique key from the data itself.