Aviary

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.

nestjs-codegen.config.ts
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 msw

Config

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.

src/generated/mocks.ts
// 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.

test/setup.ts
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());
test/users.test.ts
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:

src/mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from '../generated/mocks';

export const worker = setupWorker(...handlers);
src/main.tsx
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

On this page