Skip to content

Repository files navigation

Pactwork

Pactwork

Generate API mocks from your OpenAPI spec. They stay in sync automatically.

npm version MIT License Build Status

Why PactworkQuick StartStorybookRuntimeCI/CDRoadmap


Why Pactwork

The problem: API mocks drift from reality. You write them manually, the API evolves, and suddenly your tests pass but production breaks.

The fix: Point Pactwork at your OpenAPI spec. It generates MSW mocks, validates them continuously, and tells you the moment they fall out of sync.

Without Pactwork With Pactwork
Write mocks by hand Generate from your spec
Hope they match the API Validate automatically
Miss error states Test every response your API can return
Debug in production Catch drift in development
# One command. Mocks that match your API.
npx pactwork generate --spec ./openapi.yaml

Key Features

Feature What you get
Mock Generation Generate MSW mock handlers from any OpenAPI 2.0, 3.0, or 3.1 spec
Drift Detection Know immediately when your mocks fall out of sync with the spec
Scenario Catalog Test every error your API can return (404, 500, timeouts, etc.)
Runtime Utilities Simulate latency, flaky networks, and error sequences in tests
Storybook Addon Switch API states directly from the Storybook toolbar
Coverage Reporting See which API scenarios your Storybook stories cover
CI Gates Block deployments when mocks don't match the spec
Type Generation Get TypeScript types from your spec, always up to date

Quick Start

Install

npm install -D pactwork msw

Generate mocks

# Generate MSW mock handlers from your OpenAPI spec
npx pactwork generate --spec ./openapi.yaml --output ./src/mocks

# Include error scenarios for testing
npx pactwork generate --spec ./openapi.yaml --with-scenarios

What you get

Pactwork generates ready-to-use MSW handlers and a scenario catalog:

src/mocks/
├── handlers.ts      # MSW handlers for every endpoint in your spec
└── scenarios.ts     # Error/edge case responses (404, 500, etc.)
// handlers.ts (generated)
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('/api/users/:id', () => {
    return HttpResponse.json({ id: 1, name: 'Jane Doe', email: 'jane@example.com' })
  }),
  http.post('/api/users', () => {
    return HttpResponse.json({ id: 2, name: 'New User' }, { status: 201 })
  }),
  // ... one handler per operation in your spec
]

Use with MSW

Browser (Storybook, development):

import { setupWorker } from 'msw/browser'
import { handlers } from './mocks/handlers'

const worker = setupWorker(...handlers)
await worker.start()

Node (tests):

import { setupServer } from 'msw/node'
import { handlers } from './mocks/handlers'

const server = setupServer(...handlers)
beforeAll(() => server.listen())
afterAll(() => server.close())

Storybook Integration

Test every API state visually with @pactwork/storybook-addon.

Install

npm install -D @pactwork/storybook-addon

Configure

Register the addon in your Storybook config:

// .storybook/main.ts
export default {
  addons: ['@pactwork/storybook-addon'],
}

Then set up toolbar controls in your preview. See the full Storybook setup guide for the complete configuration.

What it does

Control Purpose
Scenario dropdown Switch between success, error, and edge case responses
Latency selector Simulate slow networks (0ms to 5s)
Network toggle Simulate offline and connection errors
Addon panel See request logs, active state, and available mock handlers

Set scenarios per story

// UserCard.stories.tsx
import { setScenario, resetState, setLatency } from '../mocks/handlers'

export const Loading: Story = {
  beforeEach: () => {
    resetState()
    setLatency(3000)
  },
}

export const NotFound: Story = {
  beforeEach: () => {
    resetState()
    setScenario('getUser', 'notFound')
  },
}

export const ServerError: Story = {
  beforeEach: () => {
    resetState()
    setScenario('getUser', 'serverError')
  },
}

Runtime Utilities

Control API behavior programmatically in your tests.

import { handlers, handlerMeta, scenarios } from './mocks'
import { applyScenario, withLatency, withSequence, pipe } from 'pactwork/runtime'

// Return a 404 for getUser
const errorHandlers = applyScenario(handlers, handlerMeta, 'getUser', scenarios.getUser.notFound)

// Slow down all responses by 500ms
const slowHandlers = withLatency(handlers, handlerMeta, 500)

// Simulate a flaky API: fail twice, then succeed
const flakyHandlers = withSequence(handlers, handlerMeta, 'getUser', [500, 500, 200])

// Compose multiple behaviors
const testHandlers = pipe(
  handlers,
  h => withLatency(h, handlerMeta, 100),
  h => applyScenario(h, handlerMeta, 'getUser', scenarios.getUser.notFound)
)

Available utilities

Utility What it does
applyScenario(handlers, meta, operationId, scenario) Replace a response with an error or edge case
withLatency(handlers, meta, ms) Add delay to all responses
withLatency(handlers, meta, operationId, ms) Add delay to one operation
withSequence(handlers, meta, operationId, statuses) Return different statuses in sequence
withRateLimit(handlers, meta, operationId, opts) Simulate rate limiting (429)
withNetworkError(handlers, meta, operationId, opts) Simulate timeout, abort, or connection errors
withSeed(handlers, meta, seed) Deterministic response data
pipe(handlers, ...transformers) Compose multiple utilities together

CLI Commands

Command What it does
pactwork generate Generate MSW mock handlers from your spec
pactwork validate Check if mocks match the spec
pactwork breaking Compare two spec versions for breaking changes
pactwork types Generate TypeScript types from your spec
pactwork scenarios List available error scenarios from your spec
pactwork coverage Report which scenarios your Storybook stories cover
pactwork can-i-deploy CI gate — block deploys when mocks drift
pactwork record Create a contract from your spec
pactwork verify Verify a contract against the current spec

Common flags

# CI mode — minimal output, strict exit codes
pactwork validate --ci

# Auto-fix — regenerate when drift is found
pactwork validate --fix

# Generate with error scenarios
pactwork generate --with-scenarios

# Compare API versions for breaking changes
pactwork breaking --old v1.yaml --new v2.yaml

# GitHub Actions annotations
pactwork validate --format github

# Coverage with minimum threshold
pactwork coverage --min-coverage 80

CI Integration

GitHub Action

- uses: adrozdenko/pactwork@v1
  with:
    spec: ./openapi.yaml

CLI in CI

steps:
  - name: Validate mocks
    run: npx pactwork validate --ci --format github

  - name: Check deployment safety
    run: npx pactwork can-i-deploy

Exit codes

Code Meaning
0 Mocks match the spec
1 Drift or breaking changes detected
2 Warnings treated as errors

Automated Drift Repair

For CI pipelines and AI agents, use this workflow to auto-fix drift:

# 1. Check for drift
pactwork validate --ci
# Exit 0 = mocks match, done
# Exit 1 = drift detected, continue

# 2. Regenerate mocks
pactwork generate

# 3. Verify the fix
pactwork validate --ci

# 4. Commit
git add src/mocks
git commit -m "fix: regenerate mocks from updated spec"

Configuration

Create pactwork.config.ts for project-wide settings:

import { defineConfig } from 'pactwork'

export default defineConfig({
  spec: { path: './api/openapi.yaml' },
  generate: {
    output: './src/mocks',
    typescript: true,
  },
})

Requirements

  • Node.js 20.11+
  • MSW 2.x
  • OpenAPI 2.0, 3.0, or 3.1 spec

Contributing

git clone https://github.com/adrozdenko/pactwork.git
npm install
npm test

See CONTRIBUTING.md for details.


License

MIT © adrozdenko

About

Contract-first API simulation framework - Mocks you can trust

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages