Skip to content

Commit fd477b1

Browse files
committed
implement mock package
1 parent 4a7fbe8 commit fd477b1

32 files changed

+3612
-72
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Single source of truth · Drop‑in for NestJS · Fast by design
1111
</p>
1212

13+
[![DeepWiki](https://img.shields.io/badge/DeepWiki-ts--oas%2Fnest--openapi-blue.svg?color=teal&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/ts-oas/nest-openapi)
1314
![GitHub License](https://img.shields.io/github/license/ts-oas/nest-openapi)
1415

1516
## Overview
@@ -28,6 +29,8 @@
2829

2930
- [**`@nest-openapi/serializer`**](https://nest-openapi.github.io/serializer/) — High‑performance response serialization based on your OpenAPI 3.x specification.
3031

32+
- [**`@nest-openapi/mock`**](https://nest-openapi.github.io/mock/) — Spec-driven mock server for generating realistic mock responses from your OpenAPI specification.
33+
3134
---
3235

3336
## Get Started
@@ -95,6 +98,40 @@ export class AppModule {}
9598

9699
Successful responses are automatically serialized. **For advanced configuration, see [the docs](https://nest-openapi.github.io/serializer/)**.
97100

101+
### @nest-openapi/mock
102+
103+
[![NPM – mock](https://img.shields.io/npm/v/%40nest-openapi%2Fmock.svg)](https://www.npmjs.com/package/%40nest-openapi%2Fmock)
104+
![NPM Unpacked Size](https://img.shields.io/npm/unpacked-size/%40nest-openapi%2Fmock.svg)
105+
106+
Install:
107+
108+
```bash
109+
npm i @nest-openapi/mock
110+
```
111+
112+
Minimal setup:
113+
114+
```typescript
115+
// app.module.ts
116+
import { Module } from "@nestjs/common";
117+
import { OpenAPIMockModule } from "@nest-openapi/mock";
118+
import * as openApiSpec from "./openapi.json";
119+
120+
@Module({
121+
imports: [
122+
OpenAPIMockModule.forRoot({
123+
specSource: { type: "object", spec: openApiSpec },
124+
enable: process.env.NODE_ENV === "development",
125+
mockByDefault: true,
126+
strategyOrder: ["examples", "jsf"],
127+
}),
128+
],
129+
})
130+
export class AppModule {}
131+
```
132+
133+
Routes return mocked responses when enabled. **For advanced configuration, see [the docs](https://nest-openapi.github.io/mock/)**.
134+
98135
---
99136

100137
## Compatibility

docs/.vitepress/config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default {
2222
{ text: "Home", link: "/" },
2323
{ text: "Validator", link: "/validator/" },
2424
{ text: "Serializer", link: "/serializer/" },
25+
{ text: "Mock", link: "/mock/" },
2526
],
2627
socialLinks: [
2728
{ icon: "github", link: "https://github.com/ts-oas/nest-openapi" },
@@ -50,6 +51,18 @@ export default {
5051
],
5152
},
5253
],
54+
"/mock/": [
55+
{
56+
text: "Mock",
57+
items: [
58+
{ text: "Overview", link: "/mock/" },
59+
{ text: "Options", link: "/mock/options" },
60+
{ text: "Decorators", link: "/mock/decorators" },
61+
{ text: "Manual Usage", link: "/mock/manual" },
62+
{ text: "Recording & Replay", link: "/mock/recording" },
63+
],
64+
},
65+
],
5366
},
5467
outline: [2, 6],
5568
search: { provider: "local" },

docs/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ hero:
1515
- theme: brand
1616
text: Serializer
1717
link: /serializer/
18+
- theme: brand
19+
text: Mock
20+
link: /mock/
1821
- theme: alt
1922
text: GitHub
2023
link: https://github.com/ts-oas/nest-openapi

docs/mock/decorators.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Decorators
2+
3+
### Per-route control
4+
5+
Use `@Mock` to control mocking behavior per route.
6+
7+
```ts
8+
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
9+
import { Mock } from "@nest-openapi/mock";
10+
11+
@Controller("users")
12+
export class UsersController {
13+
@Get(":id")
14+
@Mock({ status: 200, strategyOrder: ["mediatype-examples", "jsf"] })
15+
getUser(@Param("id") id: string) {
16+
return this.usersService.findOne(id);
17+
}
18+
19+
@Post()
20+
@Mock({ enable: false }) // Disable mocking for this route
21+
createUser(@Body() dto: CreateUserDto) {
22+
return this.usersService.create(dto);
23+
}
24+
}
25+
```
26+
27+
`@Mock` options:
28+
29+
- `enable` — Explicitly enable (`true`) or disable (`false`) mocking for this route. Overrides `mockByDefault` setting.
30+
- `strategyOrder` — Override strategy order for this route.
31+
- `delayMs` — Add simulated latency for this route.
32+
- `status` — Force specific HTTP status code (e.g., `404` for testing error scenarios).
33+
- `mediaType` — Force specific media type (e.g., `application/xml`).
34+
35+
## Precedence
36+
37+
Control mocking at multiple levels. Precedence order (highest to lowest):
38+
39+
1. **Request hints** — Headers sent with the HTTP request
40+
2. **Decorator**`@Mock({ ... })` on route handlers
41+
3. **Global config**`forRoot({ ... })` module options
42+
43+
### Request Hints
44+
45+
Control mocking from the client side using HTTP headers:
46+
47+
| Header | Type | Description |
48+
| ----------------------- | -------- | --------------------------------------------------------------------------------------------------- |
49+
| `x-mock-enable` | `string` | Explicitly enable (`"true"`) or disable (`"false"`) mocking. This is the primary control header. |
50+
| `x-mock-status` | `number` | Force specific HTTP status code. |
51+
| `x-mock-media` | `string` | Force specific media type (e.g., `application/xml`). |
52+
| `x-mock-strategy-order` | `string` | Comma-separated strategy order (e.g., `"mediatype-examples,jsf"` or `"schema-examples,primitive"`). |
53+
54+
**Examples:**
55+
56+
```ts
57+
// Enable mocking and force 404 response
58+
fetch("/users/1", {
59+
headers: {
60+
"x-mock-enable": "true",
61+
"x-mock-status": "404",
62+
},
63+
});
64+
65+
// Override strategy order to use JSF only
66+
fetch("/users/1", {
67+
headers: {
68+
"x-mock-enable": "true",
69+
"x-mock-strategy-order": "jsf",
70+
},
71+
});
72+
73+
// Disable mocking for this request
74+
fetch("/users/1", {
75+
headers: { "x-mock-enable": "false" },
76+
});
77+
```
78+
79+
Request hints override decorator settings, which override global defaults.

docs/mock/index.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Overview
2+
3+
`@nest-openapi/mock` provides spec-driven mock server capabilities for NestJS applications. Generate realistic mock responses automatically from your OpenAPI 3.x specification.
4+
5+
- **Spec-driven** — Mock responses generated directly from your OpenAPI spec
6+
- **Multiple strategies** — Examples, JSON Schema Faker, or primitive values
7+
- **Flexible** — Per-route overrides, request hints, and recording/replay
8+
9+
### Install
10+
11+
```bash
12+
npm i @nest-openapi/mock
13+
```
14+
15+
### Quick Start
16+
17+
```ts
18+
// app.module.ts
19+
import { Module } from "@nestjs/common";
20+
import { OpenAPIMockModule } from "@nest-openapi/mock";
21+
import * as openApiSpec from "./openapi.json";
22+
23+
@Module({
24+
imports: [
25+
OpenAPIMockModule.forRoot({
26+
specSource: { type: "object", spec: openApiSpec },
27+
enable: process.env.NODE_ENV === "development",
28+
mockByDefault: true, // Mock all routes by default, like a mock server
29+
strategyOrder: ["mediatype-examples", "schema-examples", "jsf"],
30+
}),
31+
],
32+
})
33+
export class AppModule {}
34+
```
35+
36+
That's it! Your routes will return mocked responses when enabled.
37+
38+
### Mock Strategies
39+
40+
The `strategyOrder` option controls how responses are generated:
41+
42+
- **`records`** — Uses previously recorded responses from disk (requires `recording.dir` configuration)
43+
- **`mediatype-examples`** — Uses `examples` from `content[mediaType].examples` in your OpenAPI spec
44+
- **`schema-examples`** — Uses `examples` (array) or `example` (single) from the schema object itself
45+
- **`jsf`** — Generates realistic fake data from JSON schemas using JSON Schema Faker
46+
- **`primitive`** — Simple deterministic values (`string``"string"`, `number``0`)
47+
- **`passthrough`** — Skips mocking and calls the real controller
48+
49+
Strategies are tried in order until one succeeds. Default: `["mediatype-examples", "schema-examples", "jsf"]`.
50+
51+
**Example Strategy Priority:**
52+
53+
1. First, try content-level examples (`mediatype-examples`)
54+
2. Then, try schema-level examples (`schema-examples`)
55+
3. Finally, generate from schema using JSF (`jsf`)
56+
57+
**Note:** To use recorded responses, add `"records"` to your `strategyOrder` and configure `recording.dir` in options.
58+
59+
**Field-by-Field Generation:**
60+
In `schema-examples` strategy, the mock service generates responses field-by-field, for example:
61+
62+
- When strategies are in order: `schema-examples``jsf``primitive`
63+
- Properties with `examples` use those examples
64+
- Properties without `examples` fall back to JSF or primitive values
65+
- This allows partial examples (some fields have `examples`, others don't)
66+
67+
## Next Steps
68+
69+
- [Configuration Options](./options.md) — Full configuration reference
70+
- [Decorator Usage](./decorators.md) — Per-route overrides and precedence
71+
- [Recording & Replay](./recording.md) — Capture and replay mock responses
72+
- [Manual Mock API](./manual.md) — Programmatic mock generation

docs/mock/manual.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Manual Mock API
2+
3+
Inject the `OpenAPIMockService` using the `OPENAPI_MOCK` token for custom mock logic in guards, filters, services, or middleware:
4+
5+
## Inject the service
6+
7+
```ts
8+
import { Injectable, Inject } from "@nestjs/common";
9+
import { OPENAPI_MOCK, OpenAPIMockService } from "@nest-openapi/mock";
10+
11+
@Injectable()
12+
export class MyService {
13+
constructor(
14+
@Inject(OPENAPI_MOCK)
15+
private readonly mock: OpenAPIMockService
16+
) {}
17+
}
18+
```
19+
20+
## Generate mock response manually
21+
22+
```ts
23+
import { ExecutionContext } from "@nestjs/common";
24+
25+
async function generateMock(ctx: ExecutionContext) {
26+
// Create a plan
27+
const plan = this.mock.tryPlan(ctx, {
28+
status: 200,
29+
strategyOrder: ["mediatype-examples", "schema-examples", "jsf"],
30+
});
31+
32+
if (!plan) {
33+
return null; // Operation not found or disabled
34+
}
35+
36+
// Generate the mock response
37+
const result = await this.mock.generate(ctx, plan);
38+
return result;
39+
}
40+
```
41+
42+
---
43+
44+
For programmatic recording control, see [Manual Recording API](./recording.md#manual-recording-api).

docs/mock/options.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Options
2+
3+
## Configuration Options
4+
5+
| Option | Type | Default | Description |
6+
| --------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
7+
| [`specSource`](#specsource) | `{ type: "object"; spec: OpenAPISpec } \| { type: "url"; spec: string } \| { type: "file"; spec: string }` || Provide your OpenAPI 3.x spec as an object, or point to it via URL or file path. |
8+
| `enable` | `boolean` | `true` | Master switch: registers the mock interceptor globally. When `false`, interceptor is not registered. |
9+
| `mockByDefault` | `boolean` | `false` | Controls default mocking behavior. When `false`, mock only when explicitly requested via headers or decorators. |
10+
| `strategyOrder` | `Array<"records" \| "mediatype-examples" \| "schema-examples" \| "jsf" \| "primitive" \| "passthrough">` | `["mediatype-examples", "schema-examples", "jsf"]` | Ordered list of strategies to attempt when generating responses. |
11+
| `defaultStatus` | `number` | `200` | Default HTTP status code when not specified elsewhere. |
12+
| `seed` | `number \| string` | `undefined` | Seed for deterministic mock generation. Useful for snapshot testing. |
13+
| `delayMs` | `number \| ((ctx: ExecutionContext) => number)` | `undefined` | Simulated network latency in milliseconds. |
14+
| [`jsf`](#jsf) | `object` | (jsf defaults) | JSON Schema Faker configuration. |
15+
| [`recording`](#recording) | `object` | `undefined` | Recording and replay configuration. |
16+
| `debug` | `boolean` | `false` | Verbose logging for troubleshooting. |
17+
18+
### `specSource`
19+
20+
| Type | Type | Typical use |
21+
| ---------- | ------------- | ------------------------------------------------ |
22+
| `"object"` | `OpenAPISpec` | Static spec object. |
23+
| `"url"` | `string` | Link to a centralized or externally hosted spec. |
24+
| `"file"` | `string` | Local file path to a json/yaml file. |
25+
26+
### `jsf`
27+
28+
JSON Schema Faker configuration:
29+
30+
| Option | Type | Default | Description |
31+
| --------------------- | --------------------------- | ------- | ---------------------------------------------- |
32+
| `alwaysFakeOptionals` | `boolean` || Include optional properties in generated data. |
33+
| `useDefaultValue` | `boolean` || Use schema `default` values when present. |
34+
| `minItems` | `number` || Minimum array size. |
35+
| `maxItems` | `number` || Maximum array size. |
36+
| `formats` | `Record<string, () => any>` || Custom format generators. |
37+
| `extend` | `(jsf: any) => void` || Advanced configuration hook. |
38+
39+
**Example:**
40+
41+
```ts
42+
jsf: {
43+
alwaysFakeOptionals: true,
44+
useDefaultValue: true,
45+
minItems: 1,
46+
maxItems: 5,
47+
formats: {
48+
uuid: () => crypto.randomUUID(),
49+
},
50+
extend: (jsf) => {
51+
jsf.option("failOnInvalidTypes", false);
52+
},
53+
}
54+
```
55+
56+
### `recording`
57+
58+
Recording and replay configuration for capturing real (non-mocked) responses. See [Recording & Replay](./recording.md) for detailed documentation.
59+
60+
| Option | Type | Default | Description |
61+
| ----------- | --------------- | ------- | -------------------------------------------- |
62+
| `dir` | `string` || Directory to store/load recordings. |
63+
| `capture` | `boolean` | `false` | Save real controller responses to disk. |
64+
| `matchBody` | `boolean` | `false` | Include request body in replay key matching. |
65+
| `redact` | `Array<string>` || Headers to redact from stored recordings. |
66+
67+
## Async Configuration
68+
69+
Use `forRootAsync()` for dependency injection:
70+
71+
```ts
72+
MockModule.forRootAsync({
73+
imports: [ConfigModule],
74+
useFactory: (config: ConfigService) => ({
75+
specSource: { type: "object", spec: config.get("OPENAPI_SPEC") },
76+
enable: config.get("MOCK_ENABLED"),
77+
strategyOrder: config.get("MOCK_STRATEGY").split(","),
78+
}),
79+
inject: [ConfigService],
80+
});
81+
```

0 commit comments

Comments
 (0)