pactwork
v1.2.1
Published
Contract-first API simulation framework - Mocks you can trust
Maintainers
Readme
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.yamlKey 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 mswGenerate 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-scenariosWhat 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: '[email protected]' })
}),
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-addonConfigure
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 80CI Integration
GitHub Action
- uses: adrozdenko/pactwork@v1
with:
spec: ./openapi.yamlCLI in CI
steps:
- name: Validate mocks
run: npx pactwork validate --ci --format github
- name: Check deployment safety
run: npx pactwork can-i-deployExit 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 testSee CONTRIBUTING.md for details.
License
MIT © adrozdenko
