Skip to main content

Installation

Install stepkit using your preferred package manager:
npm install stepkit

Your First Pipeline

Let’s build a simple pipeline that fetches user data and processes it. Create a new file and add the following code:
import { stepkit } from 'stepkit'

const pipeline = stepkit<{ userId: string }>()
  .step('fetch-user', ({ userId }) => {
    return { userName: 'John Doe', email: '[email protected]' }
  })
  .step('fetch-settings', ({ userName }) => {
    return { theme: 'dark', language: 'en' }
  })

const result = await pipeline.run({ userId: '123' })

console.log(result)
// Output: { userId: '123', userName: 'John Doe', email: '[email protected]', theme: 'dark', language: 'en' }
1

Import stepkit

Start by importing the stepkit function from the package.
import { stepkit } from 'stepkit'
2

Define your input type

Create a pipeline with a typed input. The input type flows through all steps.
const pipeline = stepkit<{ userId: string }>()
3

Add steps

Each step receives the current context and returns an object. The returned object is merged into the context for subsequent steps.
.step('fetch-user', ({ userId }) => {
  return { userName: 'John Doe', email: '[email protected]' }
})
.step('fetch-settings', ({ userName }) => {
  return { theme: 'dark', language: 'en' }
})
Notice how the second step can access userName from the first step’s output. TypeScript automatically infers all available fields.
4

Run the pipeline

Execute the pipeline with your initial input:
const result = await pipeline.run({ userId: '123' })

Understanding Context Flow

stepkit uses a context merging pattern. Each step:
  1. Receives the current context (input + all previous step outputs)
  2. Returns a plain object with new fields
  3. The output is merged into the context for the next step
This creates a cumulative context that grows as the pipeline executes:
const pipeline = stepkit<{ value: number }>()
  .step('double', ({ value }) => ({ doubled: value * 2 }))
  // Context is now: { value, doubled }
  .step('add-ten', ({ doubled }) => ({ result: doubled + 10 }))
  // Context is now: { value, doubled, result }

await pipeline.run({ value: 5 })
// Final result: { value: 5, doubled: 10, result: 20 }

Working with Async Operations

Step functions can be async. This is common when fetching data from APIs, databases, or other external sources:
import { stepkit } from 'stepkit'

const pipeline = stepkit<{ userId: string }>()
  .step('fetch-user', async ({ userId }) => {
    const response = await fetch(`https://api.example.com/users/${userId}`)
    const user = await response.json()
    return { userName: user.name, email: user.email }
  })
  .step('fetch-orders', async ({ userId }) => {
    const response = await fetch(`https://api.example.com/orders?user=${userId}`)
    const orders = await response.json()
    return { orderCount: orders.length }
  })

await pipeline.run({ userId: '123' })

Parallel Execution

Run multiple async operations concurrently by passing multiple functions to a single step:
import { stepkit } from 'stepkit'

const pipeline = stepkit<{ userId: string }>()
  .step(
    'fetch-data',
    async ({ userId }) => {
      const user = await fetchUser(userId)
      return { user }
    },
    async ({ userId }) => {
      const orders = await fetchOrders(userId)
      return { orders }
    },
    async ({ userId }) => {
      const settings = await fetchSettings(userId)
      return { settings }
    }
  )
  // All three functions run in parallel
  .step('process', ({ user, orders, settings }) => {
    return { summary: `${user.name} has ${orders.length} orders` }
  })

await pipeline.run({ userId: '123' })
Parallel functions in the same step execute concurrently using Promise.all(). Their results are merged into the context before the next step runs.

Enable Logging

See what’s happening inside your pipeline with built-in structured logging:
const pipeline = stepkit<{ userId: string }>()
  .step('fetch-user', async ({ userId }) => {
    // Simulate delay
    await new Promise(resolve => setTimeout(resolve, 100))
    return { userName: 'John' }
  })
  .step('process', ({ userName }) => {
    return { greeting: `Hello, ${userName}!` }
  })

await pipeline.run(
  { userId: '123' },
  { log: { stopwatch: true } }
)
Output:
🚀 Starting pipeline with input: { userId: "123" }

📍 Step: fetch-user
✅ fetch-user completed in 102ms
   Output: userName

📍 Step: process
✅ process completed in 0ms
   Output: greeting

⏱️  Performance Summary:
┌──────────────────────────────────────────────────────┐
│ ✅ fetch-user                                  102ms │
│ ✅ process                                       0ms │
└──────────────────────────────────────────────────────┘

📊 Statistics:
   Average: 51ms
   Slowest: fetch-user (102ms)
   Fastest: process (0ms)

⏰ Total Pipeline Time: 103ms

✨ Pipeline completed successfully

Error Handling

Control how errors propagate through your pipeline:
import { stepkit } from 'stepkit'

const pipeline = stepkit<{ userId: string }>()
  .step(
    {
      name: 'fetch-optional-data',
      onError: 'continue' // Don't throw, continue to next step
    },
    async ({ userId }) => {
      const data = await fetchExternalAPI(userId) // Might fail
      return { externalData: data }
    }
  )
  .step('process', ({ userId, externalData }) => {
    // externalData might be undefined if the previous step failed
    return {
      message: externalData
        ? `Got data for ${userId}`
        : `No external data for ${userId}`
    }
  })

await pipeline.run({ userId: '123' })
When using onError: 'continue', TypeScript automatically marks the step’s outputs as optional in subsequent steps.

What’s Next?

You’ve learned the basics of stepkit. Here’s what to explore next:

Core Concepts

Deep dive into context merging, type safety, and step configuration

Examples

See real-world patterns including branching, transforms, and checkpoints

API Reference

Complete reference for all methods and configuration options

Type Helpers

Learn how to extract types from your pipelines

Build docs developers (and LLMs) love