-
Notifications
You must be signed in to change notification settings - Fork 8
ZOD v4 JSON Schema #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,72 +4,107 @@ label: OpenAPI | |||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ::: danger | ||||||||||||||||||||||||||||||||||||||||||||||
| The SwaggerModule is currently in Alpha, maaaany features are missing. If something you need is not here yet, [please fill an issue/feature request](https://github.com/Savory/Danet-Swagger/issues) | ||||||||||||||||||||||||||||||||||||||||||||||
| This page assumes that you are familiar with are familiar with Danet's controllers. If you are not, please read the [Controllers](/overview/controllers) page first. | ||||||||||||||||||||||||||||||||||||||||||||||
| ::: | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ## Pre-requiresites | ||||||||||||||||||||||||||||||||||||||||||||||
| ## Body, Query | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Zod integration in Danet is very straightforward. First, you need to define your schemas using Zod. Then, you can use the `@Body()` and `@Query()` decorators from `@danet/zod` to validate the request body and query parameters. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| First, if you don't know how to use Danet's swagger module first read the dedicated page [here](/openapi/introduction). | ||||||||||||||||||||||||||||||||||||||||||||||
| Second, in order to generate openAPI definition from zod, you will need to extend zod using [zod-to-openapi](https://www.npmjs.com/package/@anatine/zod-openapi). As follows: | ||||||||||||||||||||||||||||||||||||||||||||||
| For example: | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||
| import { extendZodWithOpenApi } from 'zod-openapi'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| extendZodWithOpenApi(z); | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Controller, Post } from '@danet/core'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Body } from '@danet/zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Danet Swagger module uses the following versions: | ||||||||||||||||||||||||||||||||||||||||||||||
| const CreateTodoSchema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||
| title: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| description: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
| "zod": "npm:zod@3.23.8", | ||||||||||||||||||||||||||||||||||||||||||||||
| "zod-openapi": "npm:@anatine/zod-openapi@2.2.6" | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
| type CreateTodoSchema = z.infer<typeof CreateTodoSchema>; | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add missing Both snippets use Suggested fix import { Controller, Post } from '@danet/core';
import { Body } from '@danet/zod';
+import { z } from 'zod'; import { Controller, Get } from '@danet/core';
import { Query } from '@danet/zod';
+import { z } from 'zod';Also applies to: 45-53 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Last, you will need to add an openApi title attribute to your zod schema as follows, so we know how to name the model in the openAPI definition: | ||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||
| const Cat = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||
| name: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| breed: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| dob: z.date(), | ||||||||||||||||||||||||||||||||||||||||||||||
| isHungry: z.boolean().optional(), | ||||||||||||||||||||||||||||||||||||||||||||||
| hobbies: z.array(z.any()) | ||||||||||||||||||||||||||||||||||||||||||||||
| }).openapi({ | ||||||||||||||||||||||||||||||||||||||||||||||
| title: 'Cat' | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
| @Controller('todos') | ||||||||||||||||||||||||||||||||||||||||||||||
| export class TodosController { | ||||||||||||||||||||||||||||||||||||||||||||||
| constructor(private readonly todoService: TodoService) {} | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Post() | ||||||||||||||||||||||||||||||||||||||||||||||
| async create(@Body(CreateTodoSchema) createTodoDto: CreateTodoSchema) { | ||||||||||||||||||||||||||||||||||||||||||||||
| this.todoService.create(createTodoDto); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ## Body, Query | ||||||||||||||||||||||||||||||||||||||||||||||
| will automatically validate the request body against the `CreateTodoSchema` schema. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| The `SwaggerModule` searches for all `@Body()` and `@Query()` from `@danet/zod` decorators in route handlers to generate the API document. It also creates corresponding model definitions . Consider the following code: | ||||||||||||||||||||||||||||||||||||||||||||||
| You can also use the `@Query()` decorator to validate query parameters: | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||
| @Post() | ||||||||||||||||||||||||||||||||||||||||||||||
| async create(@Body(CreateTodoSchema) createTodoDto: CreateTodoSchema) { | ||||||||||||||||||||||||||||||||||||||||||||||
| this.todoService.create(createTodoDto); | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Controller, Get } from '@danet/core'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { Query } from '@danet/zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const GetTodoQuery = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||
| id: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| type GetTodoQuery = z.infer<typeof GetTodoQuery>; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Controller('todos') | ||||||||||||||||||||||||||||||||||||||||||||||
| export class TodosController { | ||||||||||||||||||||||||||||||||||||||||||||||
| constructor(private readonly todoService: TodoService) {} | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @Get(':id') | ||||||||||||||||||||||||||||||||||||||||||||||
| async getById(@Query(GetTodoQuery) query: GetTodoQuery) { | ||||||||||||||||||||||||||||||||||||||||||||||
| return this.todoService.getById(query.id); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ## Returned schema | ||||||||||||||||||||||||||||||||||||||||||||||
| ::: tip | ||||||||||||||||||||||||||||||||||||||||||||||
| Using these decorators will also allow you to generate OpenAPI documentation for your routes. More information on this can be found in the [OpenAPI](/zod/openapi) section. | ||||||||||||||||||||||||||||||||||||||||||||||
| ::: | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| You can use the `@ReturnedSchema` decorator to say what your endpoint will return : | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # ZOD Version 4 Support | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Danet now supports Zod version 4 `@danet/zod@^0.1.0`, which is a major upgrade from version 3. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ::: note | ||||||||||||||||||||||||||||||||||||||||||||||
| All previous versions of `@danet/zod` support only Zod version 3. | ||||||||||||||||||||||||||||||||||||||||||||||
| ::: | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| One of the new features is the ability to generate JSON Schema from Zod schemas and vice versa. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ### z.fromJSONSchema() | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix heading-level jump to satisfy markdown lint. Line 80 jumps to Suggested fix-### z.fromJSONSchema()
+## z.fromJSONSchema()(And keep 📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.22.0)[warning] 80-80: Heading levels should only increment by one level at a time (MD001, heading-increment) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||
| @ReturnedSchema(TodoSchema) | ||||||||||||||||||||||||||||||||||||||||||||||
| @Get(':id') | ||||||||||||||||||||||||||||||||||||||||||||||
| async getById(@Param('id') id: string): Todo { | ||||||||||||||||||||||||||||||||||||||||||||||
| return this.todoService.getById(id); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from 'zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const jsonSchema = { | ||||||||||||||||||||||||||||||||||||||||||||||
| type: 'object', | ||||||||||||||||||||||||||||||||||||||||||||||
| properties: { | ||||||||||||||||||||||||||||||||||||||||||||||
| name: { type: 'string' }, | ||||||||||||||||||||||||||||||||||||||||||||||
| age: { type: 'number' } | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| required: ['name', 'age'] | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const zodSchema = z.fromJSONSchema(jsonSchema); | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| If your route returns an array, pass `true` as the second argument of `ReturnedSchema` : | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ### z.toJSONSchema() | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||||||||||
| @ReturnedSchema(TodoSchema, true) | ||||||||||||||||||||||||||||||||||||||||||||||
| @Get() | ||||||||||||||||||||||||||||||||||||||||||||||
| async getTodos(): Todo[] { | ||||||||||||||||||||||||||||||||||||||||||||||
| return this.todoService.getAll(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from 'zod'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const zodSchema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||
| name: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||
| age: z.number() | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const jsonSchema = z.toJSONSchema(zodSchema); | ||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For Zod v4, z.toJSONSchema is an official public API, introduced as a stable first-party feature in the release notes and documented in the JSON Schema guide. z.fromJSONSchema is also an official API but marked as experimental and not part of the stable API, likely to change in future releases, as stated in the official JSON Schema documentation. Citations:
Remove or correct section claiming Zod v4 support—it does not exist in The documentation claims 🧰 Tools🪛 markdownlint-cli2 (0.22.0)[warning] 80-80: Heading levels should only increment by one level at a time (MD001, heading-increment) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| For more information on Zod's JSON Schema: https://zod.dev/json-schema. | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix duplicated wording in the intro warning.
There’s a typo: “familiar with are familiar with”.
Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents