Mock Handlers (MSW)
Emit deterministic Mock Service Worker handlers shaped to your response schemas — no faker dependency.
The codegen can emit a mocks.ts file: an array of Mock Service Worker
(msw) request handlers, one per contracted route, each returning seeded mock data shaped
to that route's response schema. This is Orval's headline feature — with one key
difference: no @faker-js/faker dependency. A tiny mulberry32-seeded value generator
is embedded verbatim into the output, so the generated module is fully self-contained and
deterministic for a given seed. It has no runtime dependency on this package.
The response shapes come from the exact same validation IR that powers the
OpenAPI export and forms.ts — so your mocks match the types your client
already expects. msw is the only peer dependency you install; the generated file imports
http / HttpResponse from it.
Enable it
Add a mocks block to your config. Opt-in like the OpenAPI export — nothing is emitted
until enabled: true.
import { defineConfig } from '@dudousxd/nestjs-codegen';
import { zodAdapter } from '@dudousxd/nestjs-codegen-zod';
export default defineConfig({
validation: zodAdapter,
contracts: { glob: 'src/**/*.controller.ts' },
codegen: { outDir: 'src/generated' },
mocks: {
enabled: true,
fileName: 'mocks.ts', // default
seed: 1, // deterministic data for a given seed
baseUrl: '', // prepended to every handler path
},
});Install msw as a dev dependency in the consuming project:
npm install -D mswConfig
Prop
Type
What the output looks like
Each contracted route becomes one http.<method>(path, resolver) handler that generates a
schema-shaped value and returns it as JSON. Streaming (@Sse()) routes instead return a
text/event-stream body carrying one generated event.
// Generated by @dudousxd/nestjs-codegen. Do not edit.
// MSW handlers returning deterministic, schema-shaped mock data.
/* eslint-disable */
// @ts-nocheck
import { http, HttpResponse } from 'msw';
const SEED = 1;
// ---------------------------------------------------------------------------
// Embedded mock-data runtime (mulberry32 PRNG + JSON-Schema value generator).
// Dependency-free: no @faker-js/faker. Deterministic for a given SEED.
// ---------------------------------------------------------------------------
/* … embedded makeRng() + generateMock() … */
// Shared component schemas referenced by $ref.
const DEFS = { /* User, CreateUserDto, … */ };
/** MSW request handlers, one per contracted route. */
export const handlers = [
// users.show
http.get('/users/:id', () => {
const value = generateMock({ $ref: '#/components/schemas/User' }, makeRng(SEED), DEFS);
return HttpResponse.json(value);
}),
// users.create
http.post('/users', () => {
const value = generateMock({ $ref: '#/components/schemas/User' }, makeRng(SEED), DEFS);
return HttpResponse.json(value);
}),
// events.stream (stream)
http.get('/events/stream', () => {
const value = generateMock({ /* … */ }, makeRng(SEED), DEFS);
const body = `data: ${JSON.stringify(value)}\n\n`;
return new HttpResponse(body, { headers: { 'Content-Type': 'text/event-stream' } });
}),
];MSW path syntax uses :param, which already matches NestJS route paths — so no
rewriting is needed (unlike the OpenAPI {param} form). Any non-standard verb falls back
to http.all(...).
Wiring MSW into tests
Point an MSW server at the generated handlers in your test setup. Because the data is
seeded, assertions on generated values are stable run-to-run.
import { setupServer } from 'msw/node';
import { handlers } from '../src/generated/mocks';
export const server = setupServer(...handlers);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());import { createApi } from '../src/generated/api';
import { createFetcher } from '@dudousxd/nestjs-client';
const api = createApi(createFetcher({ baseUrl: '' }));
it('returns a schema-shaped user', async () => {
const user = await api.users.show({ params: { id: 'u_1' } });
expect(user).toMatchObject({ id: expect.any(String), email: expect.any(String) });
});Keep mocks.baseUrl in sync with the baseUrl you give createFetcher so the request
URL matches a handler. Leaving both empty (relative) is the simplest setup.
Use the same handlers with the browser worker to mock the API while developing the
frontend with no backend running:
import { setupWorker } from 'msw/browser';
import { handlers } from '../generated/mocks';
export const worker = setupWorker(...handlers);if (import.meta.env.DEV) {
const { worker } = await import('./mocks/browser');
await worker.start();
}Programmatic API
Both the pure builder and the file emitter are exported from
@dudousxd/nestjs-codegen:
import { buildMocksFile, emitMocks } from '@dudousxd/nestjs-codegen';
import type { MocksEmitOptions } from '@dudousxd/nestjs-codegen';
// Pure — build the mocks.ts source text, no I/O:
const source: string = buildMocksFile(routes, { seed: 42, baseUrl: 'https://api.test' });
// Or write it to disk (creates outDir if needed):
await emitMocks(routes, 'src/generated', { fileName: 'mocks.ts', seed: 42 });Prop
Type