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:
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 type | Response helper |
|---|---|
application/json | HttpResponse.json() |
application/xml, *+xml | HttpResponse.xml() |
text/html | HttpResponse.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:
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.
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:
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:
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:
HttpResponseclass — usesHttpResponse.json(),.xml(),.html(),.text(), and.arrayBuffer()instead of rawResponseconstructors, as recommended by MSW. The helper is chosen based onmock.preferredContentTypewhen set, otherwise the first matching content type in your OpenAPI spec.delay()function — uses the standalonedelay()import (not the legacyctx.delay), configurable per-operation or globally.- Handler structure — each handler is a factory function returning
http.get/post/...(), which can be spread intosetupServer()orsetupWorker(). - 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:paramsyntax for correct matching.