Skip to content

Commit c4cd317

Browse files
committed
update docs and support locale config
1 parent 1c8eeb0 commit c4cd317

5 files changed

Lines changed: 272 additions & 7 deletions

File tree

docs/openapi-ts/plugins/faker.md

Lines changed: 216 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,229 @@
11
---
22
title: Faker
3-
description: Faker plugin for Hey API. Compatible with all our features.
3+
description: Generate realistic mock data factories from OpenAPI with the Faker plugin for openapi-ts. Fully compatible with all core features.
44
---
55

66
<script setup lang="ts">
7-
import FeatureStatus from '@components/FeatureStatus.vue';
7+
import Heading from '@components/Heading.vue';
88
</script>
99

10-
# Faker <span data-badge>vote</span>
11-
12-
<FeatureStatus issueNumber=1485 name="Faker" />
10+
<Heading>
11+
<h1>Faker</h1>
12+
</Heading>
1313

1414
### About
1515

1616
[Faker](https://fakerjs.dev) is a popular library that generates fake (but reasonable) data that can be used for things such as unit testing, performance testing, building demos, and working without a completed backend.
1717

18+
The Faker plugin for Hey API generates factory functions from your OpenAPI spec that return realistic mock data using `@faker-js/faker`.
19+
20+
## Features
21+
22+
- seamless integration with `@hey-api/openapi-ts` ecosystem
23+
- factory functions for reusable schema definitions and operation responses
24+
- smart property name inference for realistic data (e.g. `email` &rarr; `faker.internet.email()`)
25+
- constraint and format awareness (min/max, string formats, array bounds)
26+
- optional property and default value handling
27+
- dependency injection for custom faker instances (locale, seed)
28+
- minimal learning curve thanks to extending the underlying technology
29+
30+
## Installation
31+
32+
In your [configuration](/openapi-ts/get-started), add `@faker-js/faker` to your plugins and you'll be ready to generate faker factories. :tada:
33+
34+
```js
35+
export default {
36+
input: 'hey-api/backend', // sign up at app.heyapi.dev
37+
output: 'src/client',
38+
plugins: [
39+
// ...other plugins
40+
'@faker-js/faker', // [!code ++]
41+
],
42+
};
43+
```
44+
45+
## Output
46+
47+
The Faker plugin will generate the following artifacts, depending on the input specification.
48+
49+
## Definitions
50+
51+
A factory function is generated for every reusable definition from your input.
52+
53+
::: code-group
54+
55+
```ts [example]
56+
import { faker, type Faker } from '@faker-js/faker';
57+
58+
import type { Bar, Foo } from '../types.gen';
59+
60+
export const fakeFoo = (options?: Options): Foo => ({
61+
name: ensureFaker(options).string.sample(),
62+
age: ensureFaker(options).number.int({ min: 1, max: 120 }),
63+
});
64+
65+
export const fakeBar = (options?: Options): Bar =>
66+
ensureFaker(options).helpers.arrayElement(['baz', 'qux']);
67+
```
68+
69+
```js [config]
70+
export default {
71+
input: 'hey-api/backend', // sign up at app.heyapi.dev
72+
output: 'src/client',
73+
plugins: [
74+
// ...other plugins
75+
{
76+
name: '@faker-js/faker',
77+
definitions: true, // [!code ++]
78+
},
79+
],
80+
};
81+
```
82+
83+
:::
84+
85+
You can customize the naming and casing pattern for `definitions` factories using the `.name` and `.case` options.
86+
87+
## Responses
88+
89+
A factory function is generated for operation responses. When an operation has a single success response, the factory is unsuffixed. When multiple responses exist, each factory is suffixed with the status code.
90+
91+
::: code-group
92+
93+
```ts [example]
94+
// single success response — unsuffixed
95+
export const fakeCreatePetResponse = (options?: Options): CreatePetResponse => fakePet(options);
96+
97+
// multiple responses — suffixed with status code
98+
export const fakeGetPetResponse200 = (options?: Options): GetPetResponses[200] => fakePet(options);
99+
100+
export const fakeGetPetResponse404 = (options?: Options): GetPetErrors[404] => fakeError(options);
101+
```
102+
103+
```js [config]
104+
export default {
105+
input: 'hey-api/backend', // sign up at app.heyapi.dev
106+
output: 'src/client',
107+
plugins: [
108+
// ...other plugins
109+
{
110+
name: '@faker-js/faker',
111+
responses: true, // [!code ++]
112+
},
113+
],
114+
};
115+
```
116+
117+
:::
118+
119+
You can customize the naming and casing pattern for `responses` factories using the `.name` and `.case` options.
120+
121+
## Smart Inference
122+
123+
The plugin infers semantically appropriate faker methods from property names, producing more realistic mock data without requiring explicit schema formats or annotations.
124+
125+
```ts
126+
// property name "email" → faker.internet.email()
127+
// property name "firstName" → faker.person.firstName()
128+
// property name "city" → faker.location.city()
129+
// property name "id" → faker.string.uuid()
130+
// property name "age" → faker.number.int({ min: 1, max: 120 })
131+
```
132+
133+
Ancestor context is also used for disambiguation. For example, `name` under a `User` schema produces `faker.person.fullName()`, while `name` under a `Company` schema produces `faker.company.name()`.
134+
135+
Explicit schema annotations (format, pattern, constraints) always take priority over name-based inference.
136+
137+
## Formats and Constraints
138+
139+
The plugin respects OpenAPI schema formats and constraints to generate appropriately bounded data.
140+
141+
**String formats:**
142+
143+
- `email` &rarr; `faker.internet.email()`
144+
- `date-time` &rarr; `faker.date.recent().toISOString()`
145+
- `date` &rarr; `faker.date.recent().toISOString().slice(0, 10)`
146+
- `uuid` &rarr; `faker.string.uuid()`
147+
- `uri` / `url` &rarr; `faker.internet.url()`
148+
- `ipv4` &rarr; `faker.internet.ipv4()`
149+
- `ipv6` &rarr; `faker.internet.ipv6()`
150+
- `pattern` &rarr; `faker.helpers.fromRegExp(pattern)`
151+
152+
**Numeric constraints:**
153+
154+
- `minimum` / `maximum` &rarr; `faker.number.int({ min, max })`
155+
- `exclusiveMinimum` / `exclusiveMaximum` &rarr; adjusted bounds
156+
157+
**String constraints:**
158+
159+
- `minLength` / `maxLength` &rarr; `faker.string.alpha({ length: { min, max } })`
160+
161+
**Array constraints:**
162+
163+
- `minItems` / `maxItems` &rarr; controls count in `faker.helpers.multiple()`
164+
165+
## Optional Properties
166+
167+
Required properties are always included. Optional properties are controlled by the `includeOptional` option at runtime.
168+
169+
```ts
170+
const user = fakeFoo(); // includes optional properties by default
171+
const user = fakeFoo({ includeOptional: false }); // excludes optional properties
172+
const user = fakeFoo({ includeOptional: 0.5 }); // 50% probability per optional property
173+
```
174+
175+
## Default Values
176+
177+
When a schema defines default values, you can control whether to use them instead of generating fake data via the `useDefault` option.
178+
179+
```ts
180+
const data = fakeFoo(); // always generates fake data
181+
const data = fakeFoo({ useDefault: true }); // uses schema defaults when available
182+
const data = fakeFoo({ useDefault: 0.5 }); // 50% probability of using defaults
183+
```
184+
185+
## Locale
186+
187+
You can configure the locale for generated faker factories. When set, the generated code imports the faker instance from the locale-specific build of `@faker-js/faker`.
188+
189+
::: code-group
190+
191+
```ts [output]
192+
import { faker } from '@faker-js/faker/locale/de';
193+
import type { Faker } from '@faker-js/faker';
194+
```
195+
196+
```js [config]
197+
export default {
198+
input: 'hey-api/backend', // sign up at app.heyapi.dev
199+
output: 'src/client',
200+
plugins: [
201+
// ...other plugins
202+
{
203+
name: '@faker-js/faker',
204+
locale: 'de', // [!code ++]
205+
},
206+
],
207+
};
208+
```
209+
210+
:::
211+
212+
See the [Faker localization guide](https://fakerjs.dev/guide/localization) for available locales.
213+
214+
## Custom Faker Instance
215+
216+
For runtime locale switching or seeded deterministic output, you can pass your own faker instance via the `faker` option.
217+
218+
```ts
219+
import { faker } from '@faker-js/faker/locale/de';
220+
221+
faker.seed(42);
222+
const data = fakeFoo({ faker });
223+
```
224+
225+
## API
226+
227+
You can view the complete list of options in the [UserConfig](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/plugins/@faker-js/faker/types.ts) interface.
228+
18229
<!--@include: ../../partials/sponsors.md-->
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import type { Faker } from '@faker-js/faker';
4+
import { faker } from '@faker-js/faker/locale/de';
5+
6+
export type Options = {
7+
faker?: Faker;
8+
/**
9+
* Whether to include optional properties.
10+
* Provide a number between 0 and 1 to randomly include based on that probability.
11+
* @default true
12+
*/
13+
includeOptional?: boolean | number;
14+
/**
15+
* Whether to use schema default values instead of generating fake data.
16+
* Provide a number between 0 and 1 to randomly use defaults based on that probability.
17+
* @default false
18+
*/
19+
useDefault?: boolean | number;
20+
};
21+
22+
const resolveCondition = (condition: boolean | number, faker: Faker): boolean => condition === true || typeof condition === 'number' && faker.datatype.boolean({ probability: condition });
23+
24+
const ensureFaker = (options?: Options): Faker => options?.faker ?? faker;
25+
26+
export const fakeFoo = (options?: Options) => ({
27+
name: ensureFaker(options).string.sample(),
28+
age: ensureFaker(options).number.int({ min: 1, max: 120 }),
29+
...!resolveCondition(options?.includeOptional ?? true, ensureFaker(options)) ? {} : { active: ensureFaker(options).datatype.boolean() }
30+
});
31+
32+
export const fakeBar = (options?: Options) => ensureFaker(options).helpers.arrayElement(['baz', 'qux']);

packages/openapi-ts-tests/faker/v1/test/3.1.x.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ describe(`OpenAPI ${version}`, () => {
2222
}),
2323
description: 'generates faker factories for basic schemas',
2424
},
25+
{
26+
config: createConfig({
27+
input: 'faker-basic.yaml',
28+
output: 'faker-basic-locale',
29+
plugins: [{ locale: 'de', name: '@faker-js/faker' }],
30+
}),
31+
description: 'generates faker factories with locale-specific import',
32+
},
2533
{
2634
config: createConfig({
2735
input: 'faker-basic.yaml',

packages/openapi-ts/src/plugins/@faker-js/faker/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ export type UserConfig = Plugin.Name<'@faker-js/faker'> &
4444
*/
4545
name?: NameTransformer;
4646
};
47+
/**
48+
* Locale for `@faker-js/faker`. When set, the generated import for the
49+
* faker instance will use `@faker-js/faker/locale/{locale}` instead of
50+
* `@faker-js/faker`.
51+
*
52+
* @see https://fakerjs.dev/guide/localization
53+
*/
54+
locale?: string;
4755
/**
4856
* Configuration for operation response factories.
4957
*
@@ -85,6 +93,8 @@ export type Config = Plugin.Name<'@faker-js/faker'> &
8593
case: Casing;
8694
/** Configuration for reusable schema definitions. */
8795
definitions: NamingOptions & FeatureToggle;
96+
/** Locale for `@faker-js/faker`. */
97+
locale?: string;
8898
/** Configuration for operation response factories. */
8999
responses: NamingOptions & FeatureToggle;
90100
};

packages/openapi-ts/src/plugins/@faker-js/faker/v1/plugin.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import type { FakerJsFakerPlugin } from '../types';
77
import { createProcessor } from './processor';
88

99
export const handlerV1: FakerJsFakerPlugin['Handler'] = ({ plugin }) => {
10+
const fakerPackagePath = plugin.config.locale
11+
? `@faker-js/faker/locale/${plugin.config.locale}`
12+
: '@faker-js/faker';
13+
1014
plugin.symbol('faker', {
11-
external: '@faker-js/faker',
15+
external: fakerPackagePath,
1216
importKind: 'named',
1317
});
1418

@@ -49,7 +53,7 @@ export const handlerV1: FakerJsFakerPlugin['Handler'] = ({ plugin }) => {
4953
const resolveConditionSlot = plugin.node(null);
5054

5155
// Emit helper: const ensureFaker = (options?: Options): Faker => options?.faker ?? faker
52-
const fakerSymbol = plugin.external('@faker-js/faker.faker');
56+
const fakerSymbol = plugin.external(`${fakerPackagePath}.faker`);
5357
const ensureFakerFn = $.func()
5458
.arrow()
5559
.param('options', (p) => p.optional().type('Options'))

0 commit comments

Comments
 (0)