npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codewithagents/openapi-msw

v0.2.1

Published

Generate MSW v2 handlers with seeded Faker mock data from OpenAPI 3.1 specs

Downloads

3,488

Readme

@codewithagents/openapi-msw

npm CI codecov CodeQL

📖 Full documentation

Generate MSW v2 HTTP handlers, populated with seeded Faker mock data, from your OpenAPI 3.x spec. Point it at a spec, get a handlers.ts you can drop into a browser worker or a Node test server.

  • One handler per operation: every get, post, put, patch, and delete operation becomes an http.<method>(path, resolver) entry in an exported handlers array.
  • Seeded, deterministic mocks: faker.seed(<seed>) (default 42) is written into the generated file, so every run with the same spec and seed produces identical mock values. Change the seed, re-generate, get a new but still stable dataset.
  • MSW v2 path syntax: OpenAPI {id} path params are rewritten to MSW colon style :id. Every param in a path is converted (/orgs/{orgId}/repos/{repoId} becomes /orgs/:orgId/repos/:repoId).
  • Schema-aware bodies: each handler returns a Faker-generated body shaped from the operation's 2xx JSON response schema, honouring string formats, enums, arrays, and nested objects.
  • Prettier-clean output: the generated handlers.ts passes prettier --check out of the box.
  • OpenAPI 3.x: 3.1.x is the primary target; 3.0.x is supported in practice (8 of the 13 showcase specs are 3.0.x and all compile under tsc --strict).

Install

pnpm add -D @codewithagents/openapi-msw
# or
npm install -D @codewithagents/openapi-msw

The generated handlers.ts imports from msw and @faker-js/faker directly, so both are peer dependencies you install yourself:

pnpm add msw @faker-js/faker

| Peer dependency | Range | |---|---| | msw | ^2.0.0 | | @faker-js/faker | ^9.0.0 |

This package depends on openapi-zod-ts for spec parsing and shared config plumbing, but it does not require you to generate models or a client. Run it standalone or alongside the rest of the suite.


Quick start

1. Create openapi-msw.config.json in your project root:

{
  "input_openapi": "./spec/api.json",
  "output": "./src/mocks",
  "seed": 42,
  "max_array_items": 3,
  "depth_cap": 30
}

Only input_openapi and output are required. The three numeric fields are optional and default to the values shown above.

2. Run the generator:

npx openapi-msw
# or point at a config elsewhere:
npx openapi-msw --config ./config/openapi-msw.config.json

The CLI takes no positional arguments. Available flags are --config <path>, --help / -h, and --version / -v. When --config is given, relative paths in the config resolve from the config file's directory.

3. handlers.ts appears in your output directory:

The output directory is created recursively if it does not exist. Wire the handlers into MSW as usual:

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

export const worker = setupWorker(...handlers)
// Node / test server
import { setupServer } from 'msw/node'
import { handlers } from './src/mocks/handlers'

export const server = setupServer(...handlers)

Generated output

Given a task API with GET /api/v1/tasks (returns a paged list) and DELETE /api/v1/tasks/{id} (204 No Content), the generated handlers.ts looks like this:

// This file is auto-generated by @codewithagents/openapi-msw, do not edit
import { http, HttpResponse } from 'msw'
import { faker } from '@faker-js/faker'

faker.seed(42)

export const handlers = [
  http.get('/api/v1/tasks', () =>
    HttpResponse.json({
      items: Array.from({ length: 3 }, () => ({
        id: faker.string.uuid(),
        title: faker.lorem.word(),
        status: faker.helpers.arrayElement(['pending', 'in_progress', 'done']),
        priority: faker.number.int({ min: 1, max: 1000 }),
        assigneeEmail: faker.internet.email(),
        createdAt: faker.date.recent().toISOString(),
      })),
      total: faker.number.int({ min: 1, max: 1000 }),
    })
  ),
  http.delete('/api/v1/tasks/:id', () => HttpResponse.json(null, { status: 204 })),
]

A committed, drift-checked sample lives at packages/integration/generated/handlers.ts, generated from packages/integration/spec/api.json.


Config reference

openapi-msw.config.json:

| Field | Required | Default | Description | |---|---|---|---| | input_openapi | Yes | n/a | Path to the OpenAPI 3.x spec (JSON or YAML) | | output | Yes | n/a | Directory to write handlers.ts (created recursively) | | seed | No | 42 | Passed to faker.seed() in the generated file. Must be an integer >= 0 | | max_array_items | No | 3 | Length of arrays in generated mocks. Must be an integer >= 1 | | depth_cap | No | 30 | Schema recursion depth before bailing out to null. Must be an integer >= 1 |

The three optional keys are validated only when present.

Multi-spec generation. Replace the top-level fields with a projects array to generate one handlers.ts per spec. Mixing a projects array with top-level input_openapi / output throws.

{
  "projects": [
    { "input_openapi": "./specs/public.json", "output": "./src/mocks/public" },
    { "input_openapi": "./specs/admin.json", "output": "./src/mocks/admin" }
  ]
}

How bodies are generated

The resolver body for each handler is built from the operation's selected 2xx JSON response schema. Faker calls are chosen per schema node:

| Schema | Generated value | |---|---| | string, format email | faker.internet.email() | | string, format uuid | faker.string.uuid() | | string, format date-time | faker.date.recent().toISOString() | | string, format date | faker.date.recent().toISOString().slice(0, 10) | | string, format uri / url | faker.internet.url() | | string (other) | faker.lorem.word() | | integer, int32, int64 | faker.number.int({ min: 1, max: 1000 }) | | number (other) | faker.number.float({ min: 0, max: 1000, fractionDigits: 2 }) | | boolean | faker.datatype.boolean() | | enum | faker.helpers.arrayElement([...literals]) | | array | Array.from({ length: max_array_items }, () => item) | | object with additionalProperties | Object.fromEntries(...) with two generated entries | | null / unresolvable | null |


Non-obvious behavior

  • The seed lives in the output, not the runtime. faker.seed(<seed>) is the first executable line of the generated file. To change mock values, re-generate with a different seed. Two runs of the same spec and seed produce byte-identical handlers.
  • 2xx selection order. The generator prefers a 200 response, then 201, then the first 2xx code that declares an application/json body. A 200 emits HttpResponse.json(body) with no status argument; any other 2xx (for example 201) emits HttpResponse.json(body, { status: N }).
  • No JSON body. If the chosen 2xx response has no application/json schema, the handler emits HttpResponse.json(null, { status: N }) (status from the first 2xx code, defaulting to 204). If that response instead declares a non-JSON content type (text/plain, text/csv, application/octet-stream, image, and so on) the handler emits new HttpResponse(null, { status: N }) rather than falsely claiming application/json. Only application/json content is mocked with a faker body.
  • allOf is merged; anyOf / oneOf is not unioned. Properties of all allOf members are merged into one object and their required arrays concatenated. For anyOf / oneOf the generator picks the first concrete (non-$ref) member, or resolves the first member if all are refs.
  • depth_cap bounds recursion. The depth counter increments only on $ref resolution and on allOf / anyOf / oneOf resolution steps. Array nesting and plain property access do not count. When depth exceeds depth_cap, the node emits null /* depth cap reached */.
  • Circular refs terminate cleanly. A $ref back to an already-visited schema name emits null /* circular ref: depth cap reached */, distinct from the depth-cap comment, so self-referential schemas do not loop.
  • Type unions and missing types. For an OpenAPI 3.1 type array, the generator uses the first element. A schema with no type but with properties is treated as an object; otherwise it emits null.
  • Path-rooted guard rails. input_openapi and output paths that resolve into system directories (/etc, /usr, /bin, C:\Windows, and similar) are rejected as likely misconfiguration. This is a guard rail, not a security sandbox.

Programmatic API

The package also exports a programmatic surface for building tooling around it:

import { generateHandlers, generate, loadConfig, loadConfigs } from '@codewithagents/openapi-msw'

// Build the handlers file in memory from a parsed spec.
const { filename, content } = generateHandlers(spec, {
  seed: 42,
  maxArrayItems: 3,
  depthCap: 30,
})
// filename === 'handlers.ts'

generate runs the full config-driven flow (load config, parse spec, write files). loadConfig / loadConfigs read and validate a config file. The exported types are MswConfig, HandlerGenOptions, and GeneratedFile.


Part of the openapi-zod-ts suite

openapi-msw is one of five published packages in the codewithagents/openapi-zod-ts monorepo. Four generators emit files from a single OpenAPI spec, and one runtime helper maps API errors in app code:

| Package | Role | |---|---| | openapi-zod-ts | Generator: TypeScript models, a native fetch client, and Zod schemas | | @codewithagents/openapi-server | Generator: framework-agnostic service interface plus an optional hono \| express \| fastify \| none router (default none) | | @codewithagents/openapi-react-query | Generator: typed React Query v5 hooks over the generated client | | @codewithagents/openapi-msw | Generator: this package, MSW v2 handlers with seeded Faker mock data | | @codewithagents/api-errors | Runtime helper: map backend error responses to form-field errors |

See the full-stack reference app (Fastify + React + react-query, with a browser e2e suite) for these packages working together end to end.


License

MIT