Swagger
Use nest-zod/swagger when you want the same runtime parsing and serialization behavior as nest-zod, plus generated OpenAPI metadata.
import { ZBody, ZParam, ZQuery, ZSerialize } from 'nest-zod/swagger';What It Adds
ZBodyattaches request body metadata withApiBodyZParamattaches path parameter metadata withApiParamZQueryemits query metadata withApiQueryZSerializeattaches response metadata withApiResponse
The runtime behavior still comes from the same validation pipe and serializer interceptor model.
ZSerialize documents the effective success status:
200by default201for@Post()handlers unless you override the route status- an explicit
statusif you pass one in the decorator options
For query params:
- use
ZQuery(schema)when the whole query string maps to an object schema - use
ZQuery('name', schema)for a named query parameter, including object-shaped values
For whole-query schemas in nest-zod/swagger, the object form may be wrapped by optional, nullable, or default. More exotic wrapper chains are not part of the documented contract.
Example
import { Controller, Get, Post } from '@nestjs/common';
import { z } from 'zod';
import { ZBody, ZParam, ZQuery, ZSerialize } from 'nest-zod/swagger';
const createItemSchema = z.object({
title: z.string().trim().min(1),
});
const listItemsQuerySchema = z.object({
page: z.coerce.number().int().positive().default(1),
includeArchived: z.coerce.boolean().default(false),
});
const itemResponseSchema = z.object({
id: z.uuid(),
title: z.string(),
quantity: z.int(),
});
@Controller('items')
export class ItemsController {
@Post()
@ZSerialize(itemResponseSchema, { refId: 'CreateItemResponse' })
create(@ZBody(createItemSchema, { refId: 'CreateItemBody' }) body: z.infer<typeof createItemSchema>) {
return {
id: '550e8400-e29b-41d4-a716-446655440000',
title: body.title,
quantity: 1,
};
}
@Get(':id')
@ZSerialize(itemResponseSchema, { refId: 'GetItemResponse' })
get(@ZParam('id', z.uuid(), { refId: 'ItemId' }) id: string) {
return {
id,
title: 'Widget',
quantity: 3,
};
}
@Get()
@ZSerialize(z.object({
page: z.int(),
includeArchived: z.boolean(),
items: z.array(itemResponseSchema),
}))
list(@ZQuery(listItemsQuerySchema, { refId: 'ListItemsQuery' }) query: z.infer<typeof listItemsQuerySchema>) {
return {
page: query.page,
includeArchived: query.includeArchived,
items: [],
};
}
}For a scalar query parameter, use the named form:
@Get('search')
search(@ZQuery('q', z.string().trim().min(1)) q: string) {
return { q };
}For a named object query parameter, use the same form with an object schema:
@Get('named-query')
search(
@ZQuery('filter', z.object({ q: z.string().trim().min(1) }))
filter: { q: string },
) {
return filter;
}With the default Nest Express adapter, nested query shapes typically require the extended query parser so requests like ?filter[q]=widget arrive as objects. The local playground enables that parser.
SwaggerModule Setup
nest-zod/swagger does not register Swagger for you. Keep your normal Nest bootstrap:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder().setTitle('My API').build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
void bootstrap();If you want a working local example, see the Playground.