zod-openapi 5.0.0-beta.8 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <h1 align="center">zod-openapi</h1>
4
4
  </p>
5
5
  <p align="center">
6
- A TypeScript library which uses <a href="https://github.com/colinhacks/zod">Zod</a> schemas to generate OpenAPI v3.x documentation.
6
+ A TypeScript library which uses <a href="https://github.com/colinhacks/zod">Zod</a> schemas to generate OpenAPI v3.1x documentation.
7
7
  </p>
8
8
  <div align="center">
9
9
  <a href="https://www.npmjs.com/package/zod-openapi"><img src="https://img.shields.io/npm/v/zod-openapi"/></a>
@@ -31,33 +31,41 @@ pnpm install zod zod-openapi
31
31
 
32
32
  ### `.meta()`
33
33
 
34
- Use the `.meta()` method to add metadata to a Zod schema. It accepts an object with the following options:
34
+ Use the `.meta()` method to add OpenAPI metadata to a Zod schema. It accepts an object with the following options:
35
35
 
36
36
  | Option | Description |
37
37
  | ---------- | ---------------------------------------------------------------------------------------------------------------- |
38
38
  | `id` | Registers a schema as a reusable OpenAPI component. |
39
39
  | `header` | Adds metadata for [response headers](#response-headers). |
40
40
  | `param` | Adds metadata for [request parameters](#parameters). |
41
- | `override` | Allows you to override the rendered OpenAPI schema. This takes either an object or a function |
41
+ | `override` | Allows you to override the rendered OpenAPI schema. This takes either an object or a function. |
42
42
  | `outputId` | Allows you to set a different ID for the output schema. This is useful when the input and output schemas differ. |
43
- | `unusedIO` | Allows you to set the `io` for an unused schema added to the components section. Defaults to `output` |
43
+ | `unusedIO` | Allows you to set the `io` for an unused schema added to the components section. Defaults to `output`. |
44
+
45
+ You can also set standard OpenAPI properties directly in the `.meta()` method, such as:
46
+
47
+ ```typescript
48
+ z.string().meta({
49
+ description: 'A text field',
50
+ example: 'Example value',
51
+ });
52
+ ```
44
53
 
45
54
  ### `createDocument`
46
55
 
47
56
  Generates an OpenAPI documentation object.
48
57
 
49
58
  ```typescript
50
- import 'zod-openapi/extend';
51
- import { z } from 'zod';
59
+ import * as z from 'zod/v4';
52
60
  import { createDocument } from 'zod-openapi';
53
61
 
54
- const jobId = z.string().openapi({
62
+ const jobId = z.string().meta({
55
63
  description: 'A unique identifier for a job',
56
64
  example: '12345',
57
65
  id: 'jobId',
58
66
  });
59
67
 
60
- const title = z.string().openapi({
68
+ const title = z.string().meta({
61
69
  description: 'Job title',
62
70
  example: 'My job',
63
71
  });
@@ -170,37 +178,45 @@ const document = createDocument({
170
178
  ```
171
179
  </details>
172
180
 
173
- #### CreateDocumentOptions
174
-
175
- `createDocument` takes an optional `CreateDocumentOptions` argument which can be used to modify how the document is created.
181
+ `createDocument` takes an optional options argument which can be used to modify how the document is created
176
182
 
177
183
  ```typescript
178
- const document = createDocument(details, {
179
- override: ({ jsonSchema, zodSchema }) => {
180
- if (jsonSchema.anyOf) {
181
- ctx.jsonSchema.oneOf = ctx.jsonSchema.anyOf;
182
- delete ctx.jsonSchema.anyOf;
184
+ createDocument(doc, {
185
+ override: ({ jsonSchema, zodSchema, io }) => {
186
+ // Customize the schema generation
187
+ if (io === 'output') {
188
+ jsonSchema.type = 'string';
183
189
  }
184
190
  },
185
191
  });
186
192
  ```
187
193
 
194
+ #### CreateDocumentOptions
195
+
196
+ | Option | Type | Default | Description |
197
+ | ------------------ | ------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- |
198
+ | `override` | `Function` | `undefined` | Override rendered schema with a function`` |
199
+ | `outputIdSuffix` | `string` | `'Output'` | Suffix for output schema IDs when the schema is used in both a request and response |
200
+ | `allowEmptySchema` | `Object` | `undefined` | Control whether empty schemas are allowed. |
201
+ | `cycles` | `'ref' \| 'throw'` | `'ref'` | How to handle cycles in schemas.<br>- `'ref'` — Break cycles using $defs<br>- `'throw'` — Error on cycles |
202
+ | `reused` | `'ref' \| 'inline'` | `'inline'` | How to handle reused schemas.<br>- `'ref'` — Reused schemas as references<br>- `'inline'` — Inline reused schemas |
203
+ | |
204
+
188
205
  ### `createSchema`
189
206
 
190
207
  Creates an OpenAPI Schema Object along with any registered components. OpenAPI 3.1.0 Schema Objects are fully compatible with JSON Schema.
191
208
 
192
209
  ```typescript
193
- import 'zod-openapi/extend';
194
- import { z } from 'zod';
210
+ import * as z from 'zod/v4';
195
211
  import { createSchema } from 'zod-openapi';
196
212
 
197
- const jobId = z.string().openapi({
213
+ const jobId = z.string().meta({
198
214
  description: 'A unique identifier for a job',
199
215
  example: '12345',
200
216
  id: 'jobId',
201
217
  });
202
218
 
203
- const title = z.string().openapi({
219
+ const title = z.string().meta({
204
220
  description: 'Job title',
205
221
  example: 'My job',
206
222
  });
@@ -245,15 +261,17 @@ const { schema, components } = createSchema(job);
245
261
 
246
262
  #### CreateSchemaOptions
247
263
 
248
- `createSchema` takes an optional `CreateSchemaOptions` parameter which can also take the same options as [CreateDocumentOptions](#createdocumentoptions) along with the following options:
264
+ `createSchema` takes an optional `CreateSchemaOptions` parameter which includes all options from [CreateDocumentOptions](#createdocumentoptions) plus the following:
249
265
 
250
266
  ```typescript
251
267
  const { schema, components } = createSchema(job, {
252
- schemaType: 'input'; // This controls whether this should be rendered as a request (`input`) or response (`output`). Defaults to `output`
253
- openapi: '3.0.0'; // OpenAPI version to use, defaults to `'3.1.0'`
254
- components: { jobId: z.string() } // Additional components to use and create while rendering the schema
255
- componentRefPath: '#/definitions/' // Defaults to #/components/schemas/
256
- })
268
+ // Input/Output context - controls how schemas are generated
269
+ io: 'input', // 'input' for request bodies/params, 'output' for responses
270
+ // Component handling
271
+ schemaComponents: { jobId: z.string() }, // Pre-defined components to use
272
+ schemaComponentRefPath: '#/definitions/', // Custom path prefix for component references
273
+ opts: {}, // Create Document Options,
274
+ });
257
275
  ```
258
276
 
259
277
  ### Request Parameters
@@ -285,7 +303,7 @@ createDocument({
285
303
  '/jobs/{a}': {
286
304
  put: {
287
305
  parameters: [
288
- z.string().openapi({
306
+ z.string().meta({
289
307
  param: {
290
308
  name: 'job-header',
291
309
  in: 'header',
@@ -396,7 +414,7 @@ If we take the example in `createDocument` and instead create `title` as follows
396
414
  ##### Auto Registering Schema
397
415
 
398
416
  ```typescript
399
- const title = z.string().openapi({
417
+ const title = z.string().meta({
400
418
  description: 'Job title',
401
419
  example: 'My job',
402
420
  id: 'jobTitle', // <- new field
@@ -434,14 +452,14 @@ Another way to register schema instead of adding a `ref` is to add it to the com
434
452
  eg.
435
453
 
436
454
  ```typescript
437
- const title = z.string().openapi({
455
+ const title = z.string().meta({
438
456
  description: 'Job title',
439
457
  example: 'My job',
440
458
  });
441
459
  createDocument({
442
460
  components: {
443
461
  schemas: {
444
- jobTitle: title, // this will register this Zod Schema as jobTitle unless `ref` in `.openapi()` is specified on the type
462
+ jobTitle: title, // this will register this Zod Schema as jobTitle unless `id` in `.meta()` is specified on the type
445
463
  },
446
464
  },
447
465
  });
@@ -453,7 +471,7 @@ Query, Path, Header & Cookie parameters can be similarly registered:
453
471
 
454
472
  ```typescript
455
473
  // Easy auto registration
456
- const jobId = z.string().openapi({
474
+ const jobId = z.string().meta({
457
475
  description: 'Job ID',
458
476
  example: '1234',
459
477
  param: { id: 'jobRef' },
@@ -474,7 +492,7 @@ createDocument({
474
492
  });
475
493
 
476
494
  // or more verbose auto registration
477
- const jobId = z.string().openapi({
495
+ const jobId = z.string().meta({
478
496
  description: 'Job ID',
479
497
  example: '1234',
480
498
  param: { in: 'header', name: 'jobId', id: 'jobRef' },
@@ -490,8 +508,8 @@ createDocument({
490
508
  },
491
509
  });
492
510
 
493
- // or manual registeration
494
- const otherJobId = z.string().openapi({
511
+ // or manual registration
512
+ const otherJobId = z.string().meta({
495
513
  description: 'Job ID',
496
514
  example: '1234',
497
515
  param: { in: 'header', name: 'jobId' },
@@ -511,7 +529,7 @@ createDocument({
511
529
  Response headers can be similarly registered:
512
530
 
513
531
  ```typescript
514
- const header = z.string().openapi({
532
+ const header = z.string().meta({
515
533
  description: 'Job ID',
516
534
  example: '1234',
517
535
  header: { id: 'some-header' },
@@ -519,7 +537,7 @@ const header = z.string().openapi({
519
537
 
520
538
  // or
521
539
 
522
- const jobIdHeader = z.string().openapi({
540
+ const jobIdHeader = z.string().meta({
523
541
  description: 'Job ID',
524
542
  example: '1234',
525
543
  });
@@ -615,6 +633,121 @@ createDocument({
615
633
  });
616
634
  ```
617
635
 
636
+ #### Path Items
637
+
638
+ Path Items can also be registered
639
+
640
+ ```typescript
641
+ const pathItem: ZodOpenApiPathItemObject = {
642
+ id: 'some-path-item',
643
+ get: {
644
+ responses: {
645
+ 200: {
646
+ description: '200 OK',
647
+ content: {
648
+ 'application/json': {
649
+ schema: z.object({ a: z.string() }),
650
+ },
651
+ },
652
+ },
653
+ },
654
+ },
655
+ };
656
+
657
+ // or
658
+
659
+ createDocument({
660
+ components: {
661
+ pathItems: {
662
+ 'some-path-item': pathItem,
663
+ },
664
+ },
665
+ });
666
+ ```
667
+
668
+ #### Security Schemes
669
+
670
+ Security Schemes can be registered for authentication methods:
671
+
672
+ ```typescript
673
+ createDocument({
674
+ components: {
675
+ securitySchemes: {
676
+ bearerAuth: {
677
+ type: 'http',
678
+ scheme: 'bearer',
679
+ bearerFormat: 'JWT',
680
+ description: 'JWT Authentication',
681
+ },
682
+ },
683
+ },
684
+ });
685
+ ```
686
+
687
+ #### Links
688
+
689
+ Links can be registered to describe relationships between operations:
690
+
691
+ ```typescript
692
+ const link: ZodOpenApiLinkObject = {
693
+ id: 'getUserById',
694
+ operationId: 'getUser',
695
+ parameters: {
696
+ userId: '$request.path.id',
697
+ },
698
+ description: 'Link to get user by id',
699
+ };
700
+
701
+ // or
702
+
703
+ createDocument({
704
+ components: {
705
+ links: {
706
+ getUserById: {
707
+ operationId: 'getUser',
708
+ parameters: {
709
+ userId: '$request.path.id',
710
+ },
711
+ description: 'Link to get user by id',
712
+ },
713
+ },
714
+ },
715
+ });
716
+ ```
717
+
718
+ #### Examples
719
+
720
+ Examples can be registered to provide sample values for schemas:
721
+
722
+ ```typescript
723
+ const example: ZodOpenApiExampleObject = {
724
+ id: 'userExample',
725
+ summary: 'A sample user',
726
+ value: {
727
+ id: '123',
728
+ name: 'Jane Doe',
729
+ email: 'jane@example.com',
730
+ },
731
+ };
732
+
733
+ // or
734
+
735
+ createDocument({
736
+ components: {
737
+ examples: {
738
+ userExample: {
739
+ summary: 'A sample user',
740
+ value: {
741
+ id: '123',
742
+ name: 'Jane Doe',
743
+ email: 'jane@example.com',
744
+ },
745
+ },
746
+ },
747
+ },
748
+ });
749
+ ```
750
+
618
751
  ### Zod Types
619
752
 
620
753
  Zod types are composed of two different parts: the input and the output. This library decides which type to create based on if it is used in a request or response context.
@@ -631,13 +764,13 @@ Output:
631
764
 
632
765
  In general, you want to avoid using a registered input schema in an output context and vice versa. This is because the rendered input and output schemas of a simple Zod schema will differ, even with a simple Zod schema like `z.object()`.
633
766
 
634
- ```ts
767
+ ```typescript
635
768
  const schema = z.object({
636
769
  name: z.string(),
637
770
  });
638
771
  ```
639
772
 
640
- Input:
773
+ Input schemas (request bodies, parameters):
641
774
 
642
775
  ```json
643
776
  {
@@ -651,7 +784,7 @@ Input:
651
784
  }
652
785
  ```
653
786
 
654
- Output:
787
+ Output schemas (responses):
655
788
 
656
789
  ```json
657
790
  {
@@ -666,25 +799,30 @@ Output:
666
799
  }
667
800
  ```
668
801
 
669
- Unless you are strictly using `z.looseObject()`s or `z.strictObject()`s throughout your codebase you will likely run into issues where the input and output schemas differ. This library will do a best effort to check equality using a simple JSON.stringify() === JSON.stringify() check. If your registered schema contains dynamically created components, this will always fail.
670
-
671
- If the schemas are not equal, it will automatically handle this by outputting the `output` schema with an `Output` suffix.
802
+ When the same schema is referenced in both input and output contexts, the library generates two separate component schemas. This happens automatically when a schema with an ID is used in both contexts.
672
803
 
673
- You can override this by setting the `outputId` field with the `.meta()` method.
804
+ You can customize the output schema name by providing an `outputId`:
674
805
 
675
806
  ```ts
676
807
  const schema = z
677
808
  .object({
678
809
  name: z.string(),
679
810
  })
680
- .meta({ id: 'MyObject', outputId: 'MyObjectResponse' });
811
+ .meta({
812
+ id: 'MyObject',
813
+ outputId: 'MyObjectResponse', // Customize the output schema name
814
+ });
681
815
  ```
682
816
 
817
+ You can also set a global suffix for output schemas or use `z.looseObject()` and `z.strictObject()` to have explicit control over the schema behavior.
818
+
819
+ > **⚠️ Note:** If your registered schema contains dynamically created lazy components, they won't be reused between input and output schemas.
820
+
683
821
  ## Supported OpenAPI Versions
684
822
 
685
823
  Currently the following versions of OpenAPI are supported
686
824
 
687
- - `3.1.0`
825
+ - `3.1.0` (minimum version)
688
826
  - `3.1.1`
689
827
 
690
828
  Setting the `openapi` field will change how the some of the components are rendered.
@@ -726,9 +864,9 @@ See the library in use in the [examples](./examples/) folder.
726
864
 
727
865
  - [eslint-plugin-zod-openapi](https://github.com/samchungy/eslint-plugin-zod-openapi) - Eslint rules for zod-openapi. This includes features which can autogenerate Typescript comments for your Zod types based on your `description`, `example` and `deprecated` fields.
728
866
 
729
- ## Comparisons
867
+ ## Version Information
730
868
 
731
- ### [@asteasolutions/zod-to-openapi](./docs/comparisons.md)
869
+ For information about changes and migration from v4 to v5, see the [v5 migration guide](./docs/v5.md).
732
870
 
733
871
  ## Development
734
872
 
package/dist/api.d.mts CHANGED
@@ -1,20 +1,11 @@
1
- import { ComponentRegistry, Override, ParameterLocation, ParameterObject, ReferenceObject, createComponents, createRegistry } from "./components-DzDcF4aG.mjs";
1
+ import { ComponentRegistry, Override, ZodOpenApiBaseMetadata, ZodOpenApiMetadata, createComponents, createRegistry } from "./components-Dus8apcN.mjs";
2
2
  import { $ZodObject, $ZodType, $ZodTypes } from "zod/v4/core";
3
3
  import { core } from "zod/v4";
4
4
 
5
- //#region src/create/parameters.d.ts
6
- declare const createParameter: (parameter: $ZodType, location: {
7
- in: ParameterLocation;
8
- name: string;
9
- } | undefined, ctx: {
10
- registry: ComponentRegistry;
11
- io: "input" | "output";
12
- }, path: string[]) => ParameterObject | ReferenceObject;
13
- //#endregion
14
5
  //#region src/create/object.d.ts
15
6
  declare const unwrapZodObject: (zodType: $ZodTypes, io: "input" | "output", path: string[]) => $ZodObject;
16
7
  //#endregion
17
8
  //#region src/zod.d.ts
18
9
  declare const isAnyZodType: (schema: unknown) => schema is core.$ZodTypes;
19
10
  //#endregion
20
- export { ComponentRegistry, Override, createComponents, createParameter, createRegistry, isAnyZodType, unwrapZodObject };
11
+ export { ComponentRegistry, Override, ZodOpenApiBaseMetadata, ZodOpenApiMetadata, createComponents, createRegistry, isAnyZodType, unwrapZodObject };
package/dist/api.d.ts CHANGED
@@ -1,20 +1,11 @@
1
- import { ComponentRegistry, Override, ParameterLocation, ParameterObject, ReferenceObject, createComponents, createRegistry } from "./components-BBthgA_z.js";
1
+ import { ComponentRegistry, Override, ZodOpenApiBaseMetadata, ZodOpenApiMetadata, createComponents, createRegistry } from "./components-T2U31sZh.js";
2
2
  import { $ZodObject, $ZodType, $ZodTypes } from "zod/v4/core";
3
3
  import { core } from "zod/v4";
4
4
 
5
- //#region src/create/parameters.d.ts
6
- declare const createParameter: (parameter: $ZodType, location: {
7
- in: ParameterLocation;
8
- name: string;
9
- } | undefined, ctx: {
10
- registry: ComponentRegistry;
11
- io: "input" | "output";
12
- }, path: string[]) => ParameterObject | ReferenceObject;
13
- //#endregion
14
5
  //#region src/create/object.d.ts
15
6
  declare const unwrapZodObject: (zodType: $ZodTypes, io: "input" | "output", path: string[]) => $ZodObject;
16
7
  //#endregion
17
8
  //#region src/zod.d.ts
18
9
  declare const isAnyZodType: (schema: unknown) => schema is core.$ZodTypes;
19
10
  //#endregion
20
- export { ComponentRegistry, Override, createComponents, createParameter, createRegistry, isAnyZodType, unwrapZodObject };
11
+ export { ComponentRegistry, Override, ZodOpenApiBaseMetadata, ZodOpenApiMetadata, createComponents, createRegistry, isAnyZodType, unwrapZodObject };
package/dist/api.js CHANGED
@@ -1,8 +1,6 @@
1
- const require_zod = require('./zod-i2t01GF0.js');
2
- const require_components = require('./components-BaA49bb2.js');
1
+ const require_components = require('./components-BRLB21IO.js');
3
2
 
4
3
  exports.createComponents = require_components.createComponents;
5
- exports.createParameter = require_components.createParameter;
6
4
  exports.createRegistry = require_components.createRegistry;
7
- exports.isAnyZodType = require_zod.isAnyZodType;
5
+ exports.isAnyZodType = require_components.isAnyZodType;
8
6
  exports.unwrapZodObject = require_components.unwrapZodObject;
package/dist/api.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { isAnyZodType } from "./zod-BvA30wad.mjs";
2
- import { createComponents, createParameter, createRegistry, unwrapZodObject } from "./components-DMRr6ft0.mjs";
1
+ import { createComponents, createRegistry, isAnyZodType, unwrapZodObject } from "./components-DuIz-WUQ.mjs";
3
2
 
4
- export { createComponents, createParameter, createRegistry, isAnyZodType, unwrapZodObject };
3
+ export { createComponents, createRegistry, isAnyZodType, unwrapZodObject };