OrvalOrval

MSW

Generate Mock Service Worker handlers from OpenAPI

Generate MSW (Mock Service Worker) handlers from your OpenAPI specification to mock your API during development and testing.

For mock data factories without MSW request handlers, see the Faker guide.

Configuration

Set the mock option to true (emits both MSW handlers and Faker factories), or scope it to MSW only via the generator entry:

orval.config.ts
import { defineConfig } from 'orval';

export default defineConfig({
  petstore: {
    output: {
      mode: 'single',
      target: './src/api/petstore.ts',
      schemas: './src/api/model',
      mock: {
        generators: [{ type: 'msw' }],
      },
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

Generated Output

The MSW generator emits two types of functions per operation, plus an aggregator. Mock data (the get<Op>ResponseMock factories) is produced by the Faker generator — see the Faker guide for details on overriding values and formats.

1. Request Handlers

Functions that bind mock data to MSW http.* handlers using the recommended HttpResponse class:

import { HttpResponse, delay, http } from 'msw';
import type { RequestHandlerOptions } from 'msw';

export const getShowPetByIdMockHandler = (
  overrideResponse?:
    | Pet
    | ((info: Parameters<Parameters<typeof http.get>[1]>[0]) => Promise<Pet> | Pet),
  options?: RequestHandlerOptions,
) => {
  return http.get('*/pets/:petId', async (info) => {
    await delay(1000);
    return HttpResponse.json(
      overrideResponse !== undefined
        ? typeof overrideResponse === 'function'
          ? await overrideResponse(info)
          : overrideResponse
        : getShowPetByIdResponseMock(),
      { status: 200 },
    );
  }, options);
};

Orval uses the content type from your OpenAPI spec to pick the right HttpResponse helper. If multiple content types are defined, you can set a preferred default; otherwise the spec order is used:

Content typeResponse helper
application/jsonHttpResponse.json()
application/xml, *+xmlHttpResponse.xml()
text/htmlHttpResponse.html()
text/plain, other text/*HttpResponse.text()
application/octet-stream, image/*, etc.HttpResponse.arrayBuffer()
No body (204, etc.)new HttpResponse(null, { status })

To prefer a specific content type when several are defined (for example, choose JSON over XML), set mock.preferredContentType:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mock: {
        generators: [
          {
            type: 'msw',
            preferredContentType: 'application/json',
          },
        ],
      },
    },
  },
});

preferredContentType accepts common MIME literals and any custom string (via a loose (string & {}) fallback), so vendor-specific types are supported too.

preferredContentType only selects a declared media type — it does not convert the payload. If the chosen media type is not among the operation's responses, the preference is ignored and spec order is used. When the response schema is a structured object or array, prefer a JSON media type: pairing a non-JSON preference (for example, text/plain or application/xml) with a structured schema serves a stringified body instead of the structured mock, and application/octet-stream falls back to JSON with the selected media type as the Content-Type.

Array Item Factories

Set arrayItems: true on the MSW generator entry to emit reusable mock factories for object-like array item schemas in operation responses (for example, getTenantResponseModelDtoMock for value: TenantResponseModelDto[]). The factories are written into the same .msw.ts / endpoints mock file as the handlers and response mocks. See the Faker guide — Array Item Factories for configuration details and supported shapes.

orval.config.ts
mock: {
  generators: [
    {
      type: 'msw',
      arrayItems: true,
    },
  ],
}

2. Aggregated Handlers

Functions that combine all handlers for easy setup:

export const getPetsMock = () => [
  getListPetsMockHandler(),
  getCreatePetsMockHandler(),
  getShowPetByIdMockHandler(),
];

Usage

Basic Setup

Use the aggregated handler functions with MSW's setupServer (Node.js) or setupWorker (browser):

import { getPetsMock } from './api/petstore.msw';
import { setupServer } from 'msw/node';

const server = setupServer(...getPetsMock());

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Custom Response

Pass static data directly:

const showPetByIdMockHandler = getShowPetByIdMockHandler({
  id: 1,
  name: 'Custom Pet',
  tag: 'custom',
});

Dynamic Response

Pass a function for dynamic responses based on request data:

getShowPetByIdMockHandler(async (info) => {
  const petId = info.params.petId;
  return { id: Number(petId), name: `Pet ${petId}`, tag: 'dynamic' };
});

Override with server.use() (MSW best practice)

Following the MSW best practice for network behavior overrides, use server.use() to override specific handlers in individual tests while keeping the default happy-path handlers:

import { http, HttpResponse } from 'msw';

it('handles server error', () => {
  server.use(
    getShowPetByIdMockHandler(() => {
      throw new HttpResponse(null, { status: 500 });
    }),
  );

  // Test error UI...
});

Passing MSW RequestHandlerOptions

Each generated handler accepts an optional second options parameter (e.g. { once: true }):

server.use(
  getShowPetByIdMockHandler(myOverride, { once: true }),
);

Testing with Vitest

import { expect, vi } from 'vitest';
import { getShowPetByIdMockHandler, getShowPetByIdResponseMock } from './api/petstore.msw';

const mockFn = vi.fn();

const handlers = [
  getShowPetByIdMockHandler(async (info) => {
    mockFn(info.params.petId);
    return getShowPetByIdResponseMock();
  }),
];

// ... run test

expect(mockFn).toHaveBeenCalledWith('123');

Base URL

By default, handlers use a wildcard * prefix (e.g. */pets/:petId) so they match any host. To use a specific base URL, set baseUrl on the MSW generator entry:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mock: {
        generators: [
          {
            type: 'msw',
            baseUrl: 'https://api.example.com',
          },
        ],
      },
    },
  },
});

This produces handlers like http.get('https://api.example.com/pets/:petId', ...), which aligns with MSW's recommendation to use absolute URLs for precise matching.

Dynamic Imports

Enable mock.indexMockFiles to emit a root-level index.<ext>.ts file for each generator entry in split and tags-split modes. The MSW entry produces an index.msw.ts that can be dynamically imported. This keeps MSW in a dedicated barrel that your production/model barrels never re-export — handy under jsdom/vitest, where MSW references stream globals at module-evaluation time:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      mock: {
        indexMockFiles: true,
        generators: [{ type: 'msw' }],
      },
    },
  },
});
// node.ts
import * as mocks from './endpoints/index.msw';
import { setupServer } from 'msw/node';

const handlers = Object.entries(mocks).flatMap(([, getMock]) => getMock());
const server = setupServer(...handlers);

export { server };

If both msw and faker generators are configured with indexMockFiles: true, you also get an index.faker.ts alongside index.msw.ts.

MSW Best Practices

The generated code follows MSW best practices:

  • HttpResponse class — uses HttpResponse.json(), .xml(), .html(), .text(), and .arrayBuffer() instead of raw Response constructors, as recommended by MSW. The helper is chosen based on mock.preferredContentType when set, otherwise the first matching content type in your OpenAPI spec.
  • delay() function — uses the standalone delay() import (not the legacy ctx.delay), configurable per-operation or globally.
  • Handler structure — each handler is a factory function returning http.get/post/...(), which can be spread into setupServer() or setupWorker().
  • Runtime overrides — use MSW's server.use() to override individual handlers in tests. The generated handler factories make this easy.
  • No query parameters in predicates — path predicates never include query strings, per MSW's guidance.
  • Path parameters — OpenAPI {param} is converted to MSW's :param syntax for correct matching.

On this page