Skip to main content
Step is a core concept for dividing Workflows into smaller execution units. Each Step executes independently and can be retried from the point of failure, enabling safe processing of long-running tasks.

Why Steps Are Needed

Without Steps in a Workflow, if something fails midway, you have to restart from the beginning. With Steps, you can resume from where it failed.
Benefits of Using Steps:
  • Partial Retry: Re-execute only from the failed step
  • Progress Tracking: Check execution time and status of each step
  • Easier Debugging: Clearly identify which step failed

step.define()

step.define() is the most basic way to wrap a function as a step. When you make an async function a step, its execution history is saved to the database.
Key Concepts:
  • name: Step identifier (must be unique within the workflow)
  • Async function: Code that performs the actual work
  • .run(): Executes the step and returns the result

Using Return Values

You can use the return value of a Step in subsequent steps. Each step is saved independently, but data is passed in memory.
Data Flow:
  1. fetch_data step returns data
  2. data is passed in memory to transform step
  3. transformed is passed in memory to save step
When a Step is retried, previous step results are restored from the database.

step.get()

step.get() is a convenient way to execute a Model method as a step. You don’t need to wrap the function separately, making the code more concise.
Use Cases:
  • Reuse Model business logic
  • Eliminate code duplication
  • Maintain type safety

Custom Name

You can explicitly specify a Step name to make logs more readable.

Using Return Values

Model method return values work the same way.

step.sleep()

step.sleep() is a step that waits for a specified duration. Use it for delays between retries, avoiding API rate limits, periodic polling, and more.
Supported Time Formats: Practical Uses:
  • Retry delay: Wait 5 seconds after failure
  • Rate limiting: Wait 1 second between API calls
  • Batch interval: Rest 10 seconds after processing 100 items
step.sleep() is also a step, so it’s recorded in the database. On retry, the sleep won’t be re-executed.

Practical Examples

1. Batch Processing

When processing large amounts of data, divide into smaller batches. Making each batch a separate step means already processed batches won’t be re-run on failure.
Key Points:
  • Process in batches of 100
  • Each batch is an independent step
  • Progress logged

2. External API Calls

External API calls can fail due to network issues. With Steps, you can retry only the failed API call.
Benefits:
  • On API call failure, skip data normalization
  • On normalization failure, don’t re-call the API
  • Can measure execution time for each stage

3. File Processing Pipeline

A pipeline that downloads a file, parses it, and saves it. Dividing each stage into steps makes debugging and retrying easier.
Pipeline Structure:
  1. Download -> 2. Parse -> 3. Validate -> 4. Save
  2. On failure, retry from that step
  3. File is downloaded only once

4. API with Rate Limiting

When an API has rate limits, add delays between requests. Using step.sleep() prevents the delay from repeating on retry.
Rate Limiting Strategy:
  • Wait 100ms between each notification
  • Prevent API server overload
  • On retry, skip already successful notifications

Step Naming Conventions

Use Unique Names

Step names must be unique within the same workflow. Using duplicate names will only execute the last step.

Dynamic Names in Loops

When creating steps in loops, include the index to make names unique.
Naming Tips:
  • Use nouns describing the task: fetch_user, send_email
  • Include index: process_batch_0, process_batch_1
  • Snake case recommended: send_welcome_email

Important Notes

Step Best Practices:
  1. Unique Names: Step names must be unique within the workflow.
  2. Execution Order: Steps execute in the order written in code.
  3. Error Propagation: When a step fails, the workflow stops. Wrap optional tasks in try-catch.
  4. Type Inference: TypeScript automatically infers step return types.
  5. Sleep Duration Limits: Very long sleeps are not recommended as they maintain database connections.

Next Steps

@workflow Decorator

Define and schedule workflows

Error Handling

Learn retry patterns and compensating transactions