Batching & Parallel Processing
Use batching when you need to process a large list of items in parallel while controlling concurrency. Items are split into fixed-size batches, each batch runs concurrently, and failures in one batch don't affect others.
When to use this
- Bulk data imports (contacts, orders, or products from a comma-separated values (CSV) file)
- Processing hundreds or thousands of items against external APIs
- Calling rate-limited APIs where you need to control concurrency
- Any fan-out where you want failure isolation between groups
- High-concurrency fan-out, where one flat
Promise.allover the whole list would put more work in flight than your downstream services or the run's event log should carry at once
How it works
- Records are split into fixed-size batches.
- Each batch runs in parallel through
Promise.allSettled, so failures in one record don't affect others. - A
sleep()between batches paces requests to avoid overloading downstream services. - After all batches, a summary is returned with succeeded/failed counts.
Pattern
The workflow splits records into chunks, processes each chunk concurrently, tracks results per batch, and returns a final tally.
Step function
Each record is processed in its own step with full Node.js access and automatic retries.
Adapting to your use case
- Replace the
Recordtype with your actual data shape, such as orders, images, or products. - Replace
processRecord()with your import logic, such as database upserts, API calls, or file processing. - Tune
batchSizeand thesleep()duration to match your downstream rate limits. - Add or remove tracking as needed; the pattern works with any item type.
Tips
- Use
Promise.allSettledinstead ofPromise.all: Use this pattern when you want to continue even if some items fail.Promise.allrejects on the first failure, whileallSettledwaits for everything and identifies failures. - Tune batch size to your downstream API limits: If the API allows 10 concurrent requests, use
batchSize: 10. - Batching bounds concurrency, not the run's total size: Every batch still appends to the same event log, so a long enough list walks one run toward its run limits no matter how small the batches are. To shrink the run itself, bundle more items per step so one step covers many items, or spawn a child workflow per batch. Reach for one of those once a single run would grow past a few thousand events.
- Add pacing with
sleep(): Add a delay between batches to respect rate limits. The sleep is durable and survives cold starts. - Treat each
processRecordcall as an independent step: If one call fails, it retries up to three times without affecting other items in the batch.
Key APIs
"use workflow": Marks the orchestrator function."use step": Marks functions that run with full Node.js access.sleep(): Adds a pacing delay between batches.Promise.allSettled(): Runs items in parallel and isolates failures.