Comments (12)
I had the same issue,
Temporary workaround was to basically add ApiExtraModels
and explode:true
properties.
@Get('/echo')
@ApiOkResponse({
description: 'Returns `Echo: <name>` indicating the service is functional',
})
@ApiExtraModels(EchoDto)
@ApiQuery({
required: true,
name: 'queryParams',
explode: true,
type: 'object',
schema: {
$ref: getSchemaPath(EchoDto),
},
})
getEcho(@Query() queryParams: EchoDto): string {
return this.appService.getEcho(queryParams.text);
}
from nestjs-zod.
Same issue here, seems to be working only for body params, but not for params, query and headers 👀
from nestjs-zod.
I managed to solve this in a similar way. By getting the openAPI schema:
const query = z.object({
limit: z.coerce.number().int().min(10).max(100),
offset: z.coerce.number().int().min(0),
});
export const querySchema: SchemaObject = zodToOpenAPI(query);
I was able to to do this:
@Get()
@ApiQuery({ name: 'offset', schema: querySchema.properties.offset })
@ApiQuery({ name: 'limit', schema: querySchema.properties.limit })
@ZodSerializerDto(ResponseDto)
async getAll(@Query() { limit, offset }: QueryDto): Promise<Todo[]> {
return await this.service.getAll(limit, offset);
}
This way you end up with cleaner params:
And you can do the same for route params:
@Get(':id')
@ApiParam({ name: 'id', schema: paramsSchema.properties.id })
@ZodSerializerDto(ResponseDto)
async getById(@Param() { id }: ParamsDto): Promise<Todo> {
return await this.service.getById(id);
}
from nestjs-zod.
@anatine/zod-nestjs
This works.
from nestjs-zod.
Я также столкнулся с этой проблемой. Я хотел перейти с класс-валидатора на зод, но для меня этот вопрос весьма существенен.
import { ZodObject } from 'zod'; import { zodToOpenAPI } from 'nestjs-zod'; import { applyDecorators } from '@nestjs/common'; import { ApiQuery } from '@nestjs/swagger'; export const ApiQueryAll = (zodObject: ZodObject<any>) => { const openApi = zodToOpenAPI(zodObject); const decorators: MethodDecorator[] = []; if (typeof openApi.properties === 'object') { Object.entries(openApi.properties).forEach(([key, value]) => { decorators.push( ApiQuery({ name: key, schema: value, required: (openApi.required ?? []).includes(key), description: 'description' in value ? value.description : '', }), ); }); } return applyDecorators(...decorators); }; // controller @Get() @ApiOperation({ summary: '商品列表' }) @ApiQueryAll(findItemDto) @ApiOkResponseData(ItemSchema, { isArray: true, isPager: true }) findAll( @Query() dto: FindItemDto, @GetPayload() payload: Payload, ): Promise<Result<PaginatedDto<ItemSchema>>> { return this.service.findAll(payload.tenant!, dto); } // dto import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; import { bool, id, ids, page, pageSize } from '../../zod/type'; export const findItemDto = z.object({ name: z.string().optional(), code: z.string().optional()), categoryIds: ids.optional(), isTemplate: bool.optional(), templateId: id.optional(), disable: bool.optional(), allowNegativeStock: bool.optional(), purchaseAllow: bool.optional(), salesAllow: bool.optional(), page: page, pageSize: pageSize, }); export class FindItemDto extends createZodDto(findItemDto) {}What do ApiOkResponseData and GetPayload do? Can you send their code?
ApiOkResponseData is a custom response body decorator, and GetPayload is a decorator that obtains the tenant token in the request parameters. They have nothing to do with Query.
from nestjs-zod.
I think the problem is that parameters and query require @ApiProperty()
decorator on DTO's fields. Maybe we can solve this on patch level, but I'm not sure.
from nestjs-zod.
As suggested by the NestJS author, we can use a static _OPENAPI_METADATA_FACTORY
method to make it work for params and query.
This way there's no need to call patchNestJsSwagger
. (This workaround may also work for #12.)
import { Body, Controller, Get, Param, Query } from '@nestjs/common';
import { ApiOkResponse, ApiProperty } from '@nestjs/swagger';
import { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
import { SchemaObjectMetadata } from '@nestjs/swagger/dist/interfaces/schema-object-metadata.interface';
import { zodToOpenAPI } from 'nestjs-zod';
import { z } from 'nestjs-zod/z';
// I'm not sure this is enough
const toSchemaObjectMetadata = (schema: SchemaObject): SchemaObjectMetadata =>
Object.fromEntries(
Object.entries(schema.properties).map(([key, value]) => [
key,
{
...value,
required: (schema.required ?? []).includes(key),
},
]),
);
const createZodClass = <T extends z.ZodObject<any>>(schema: T) => {
return class {
static _OPENAPI_METADATA_FACTORY = () =>
toSchemaObjectMetadata(zodToOpenAPI(schema));
};
};
class MyParam extends createZodClass(
z.object({
id: z.number().int().positive(),
}),
) {
@ApiProperty({ type: () => Number })
id2: number;
}
class MyQuery extends createZodClass(
z.object({
key: z.string().uuid().optional().describe('foo'),
}),
) {}
class MyBody extends createZodClass(
z.object({
key2: z.enum(['bar', 'baz']),
}),
) {}
class MyRes extends createZodClass(
z.object({
key3: z.string().uuid().optional(),
key4: z.array(z.string()),
}),
) {}
@Controller()
export class AppController {
@Get('/:id/:id2')
@ApiOkResponse({ type: MyRes })
root(
@Param() params: MyParam,
@Query() query: MyQuery,
@Body() body: MyBody,
) {
return 42;
}
}
from nestjs-zod.
正如 NestJS 作者所建议的那样,我们可以使用静态方法来使其适用于 params 和 query。这样就没有必要调用 .(此解决方法也可能适用于 #12。
_OPENAPI_METADATA_FACTORY``patchNestJsSwagger
import { Body, Controller, Get, Param, Query } from '@nestjs/common'; import { ApiOkResponse, ApiProperty } from '@nestjs/swagger'; import { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import { SchemaObjectMetadata } from '@nestjs/swagger/dist/interfaces/schema-object-metadata.interface'; import { zodToOpenAPI } from 'nestjs-zod'; import { z } from 'nestjs-zod/z'; // I'm not sure this is enough const toSchemaObjectMetadata = (schema: SchemaObject): SchemaObjectMetadata => Object.fromEntries( Object.entries(schema.properties).map(([key, value]) => [ key, { ...value, required: (schema.required ?? []).includes(key), }, ]), ); const createZodClass = <T extends z.ZodObject<any>>(schema: T) => { return class { static _OPENAPI_METADATA_FACTORY = () => toSchemaObjectMetadata(zodToOpenAPI(schema)); }; }; class MyParam extends createZodClass( z.object({ id: z.number().int().positive(), }), ) { @ApiProperty({ type: () => Number }) id2: number; } class MyQuery extends createZodClass( z.object({ key: z.string().uuid().optional().describe('foo'), }), ) {} class MyBody extends createZodClass( z.object({ key2: z.enum(['bar', 'baz']), }), ) {} class MyRes extends createZodClass( z.object({ key3: z.string().uuid().optional(), key4: z.array(z.string()), }), ) {} @Controller() export class AppController { @Get('/:id/:id2') @ApiOkResponse({ type: MyRes }) root( @Param() params: MyParam, @Query() query: MyQuery, @Body() body: MyBody, ) { return 42; } }
Controller dto parameter is missing type and global validation pipeline is not working
from nestjs-zod.
I'm not sure why you would really want Param to use NestJS as it's more idiomatic to use the various Pipes just on that Param, ex: @Param('myParam', ParseIntPipe) myParam: number
.
However, the lack of support for @Query
is (IMO) a critical issue and we are currently keeping all our query DTOs as class-transformer
until it's resolved.
from nestjs-zod.
I also encountered this problem. I wanted to switch from class-validator to zod, but this issue is quite significant for me.
from nestjs-zod.
I also encountered this problem. I wanted to switch from class-validator to zod, but this issue is quite significant for me.
import { ZodObject } from 'zod';
import { zodToOpenAPI } from 'nestjs-zod';
import { applyDecorators } from '@nestjs/common';
import { ApiQuery } from '@nestjs/swagger';
export const ApiQueryAll = (zodObject: ZodObject<any>) => {
const openApi = zodToOpenAPI(zodObject);
const decorators: MethodDecorator[] = [];
if (typeof openApi.properties === 'object') {
Object.entries(openApi.properties).forEach(([key, value]) => {
decorators.push(
ApiQuery({
name: key,
schema: value,
required: (openApi.required ?? []).includes(key),
description: 'description' in value ? value.description : '',
}),
);
});
}
return applyDecorators(...decorators);
};
// controller
@Get()
@ApiOperation({ summary: '商品列表' })
@ApiQueryAll(findItemDto)
@ApiOkResponseData(ItemSchema, { isArray: true, isPager: true })
findAll(
@Query() dto: FindItemDto,
@GetPayload() payload: Payload,
): Promise<Result<PaginatedDto<ItemSchema>>> {
return this.service.findAll(payload.tenant!, dto);
}
// dto
import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
import { bool, id, ids, page, pageSize } from '../../zod/type';
export const findItemDto = z.object({
name: z.string().optional(),
code: z.string().optional()),
categoryIds: ids.optional(),
isTemplate: bool.optional(),
templateId: id.optional(),
disable: bool.optional(),
allowNegativeStock: bool.optional(),
purchaseAllow: bool.optional(),
salesAllow: bool.optional(),
page: page,
pageSize: pageSize,
});
export class FindItemDto extends createZodDto(findItemDto) {}
from nestjs-zod.
Я также столкнулся с этой проблемой. Я хотел перейти с класс-валидатора на зод, но для меня этот вопрос весьма существенен.
import { ZodObject } from 'zod'; import { zodToOpenAPI } from 'nestjs-zod'; import { applyDecorators } from '@nestjs/common'; import { ApiQuery } from '@nestjs/swagger'; export const ApiQueryAll = (zodObject: ZodObject<any>) => { const openApi = zodToOpenAPI(zodObject); const decorators: MethodDecorator[] = []; if (typeof openApi.properties === 'object') { Object.entries(openApi.properties).forEach(([key, value]) => { decorators.push( ApiQuery({ name: key, schema: value, required: (openApi.required ?? []).includes(key), description: 'description' in value ? value.description : '', }), ); }); } return applyDecorators(...decorators); }; // controller @Get() @ApiOperation({ summary: '商品列表' }) @ApiQueryAll(findItemDto) @ApiOkResponseData(ItemSchema, { isArray: true, isPager: true }) findAll( @Query() dto: FindItemDto, @GetPayload() payload: Payload, ): Promise<Result<PaginatedDto<ItemSchema>>> { return this.service.findAll(payload.tenant!, dto); } // dto import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; import { bool, id, ids, page, pageSize } from '../../zod/type'; export const findItemDto = z.object({ name: z.string().optional(), code: z.string().optional()), categoryIds: ids.optional(), isTemplate: bool.optional(), templateId: id.optional(), disable: bool.optional(), allowNegativeStock: bool.optional(), purchaseAllow: bool.optional(), salesAllow: bool.optional(), page: page, pageSize: pageSize, }); export class FindItemDto extends createZodDto(findItemDto) {}
What do ApiOkResponseData and GetPayload do? Can you send their code?
from nestjs-zod.
Related Issues (20)
- Error with Exported Variable and Type Inference HOT 1
- A few ideas HOT 3
- DTO is not a class, all fields typed as optional HOT 2
- Is it possible to have transformed values in the Controllers after the DTOs pass validation? HOT 1
- Enum type issue HOT 1
- Fix: dateString regex issue
- Union type issue HOT 5
- Asking for Binary zod type
- NestJS Interceptor receives a Promise instead of resolved data HOT 1
- Zod + nestjs + prisma (all optional fields) HOT 2
- Support setErrorMap HOT 1
- Add support to `.brand` schemas HOT 1
- Ghost Dependency: RxJS
- ZodSerializerDto isn't used for OpenAPI HOT 1
- z.string().datetime() is not generated correctly the format is missed in the openapi spec HOT 2
- Date is not shown in Swagger doc.
- ZodDate does not generate type and format for OpenApi HOT 1
- How do I show the validation errors when using a global pipe? HOT 1
- ZodSerializerInterceptor does not work, because there is no name attribute
- How can `z.dateString()` be used for date-revival ?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nestjs-zod.