Validation
@AsyncApiMessage and @AsyncApiHeaders turn a payload into JSON Schema for the
generated document. The package supports both validation worlds Nest users
already use, and never introduces a parallel schema reflector.
class-validator DTOs (default)
Pass a DTO class. The generator resolves it through the same @nestjs/swagger
chain that documents HTTP bodies, so the event-side schema matches the HTTP-side
schema for the same DTO. Document the DTO with @ApiProperty (and validate at
runtime with class-validator as usual).
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber } from 'class-validator';
export class OrderPlacedDto {
@ApiProperty()
@IsString()
orderId!: string;
@ApiProperty()
@IsNumber()
total!: number;
}
@AsyncApiMessage(OrderPlacedDto, { name: 'OrderPlaced' })
@AsyncApiHeaders(OrderHeadersDto)
publishOrderPlaced(): void {}
The DTO is registered once under components.schemas, including any nested DTOs
it references, and the message's payload is a $ref to it. A DTO reused across
several messages is emitted a single time.
This path requires the optional peer @nestjs/swagger. If it is missing, the
generator throws an actionable error telling you to install it or pass a
pre-computed schema instead.
Zod payloads (optional)
For applications using Zod, pass the Zod schema directly in a
{ name, schema } source. The generator detects it (through the Standard
Schema marker Zod 4 implements) and converts it with Zod 4's native
z.toJSONSchema() in the draft-07 dialect AsyncAPI 3.0 documents default to —
the event contract is written once, in Zod.
import { z } from 'zod';
import { ZodSchemaSource } from '@nest-native/asyncapi';
export const MetricReportedSchema = z.object({
name: z.string().min(1),
value: z.number(),
unit: z.enum(['ms', 'count', 'bytes']),
reportedAt: z.iso.datetime(),
});
export const metricReportedMessage: ZodSchemaSource = {
name: 'MetricReported',
schema: MetricReportedSchema,
};
@AsyncApiMessage(metricReportedMessage, { summary: 'A single metric sample.' })
publishMetricReported(): void {}
zod (^4) is an optional peer, required lazily only when a Zod source is
actually registered — never at module load. Registering a Zod source without
zod installed (or with a pre-4 major, which lacks the native
z.toJSONSchema()) fails with an actionable error.
Need custom conversion options (metadata registries, io: 'input', overrides,
a different target dialect)? Convert yourself and pass the result as a
pre-computed JSON Schema — the generator registers it verbatim, so any
Zod-to-JSON-Schema converter works:
export const metricReportedMessage: JsonSchemaSource = {
name: 'MetricReported',
schema: z.toJSONSchema(MetricReportedSchema, { target: 'draft-7' }),
};
Mixing Both
Both styles coexist in one application. The showcase sample uses class-validator DTOs for orders and a Zod payload for shipments, in the same document. Pick per message based on how that part of the application already validates.
Name Collisions
Schema and message names must be unique. Registering two structurally different schemas (or messages) under the same name is a build failure, surfacing an accidental collision instead of silently overwriting a definition. Rename one of the DTOs or schema sources so each component name is unique.
Content Type
@AsyncApiMessage defaults contentType to application/json. Override it when
a message uses a different encoding:
@AsyncApiMessage(AvroPayloadDto, { contentType: 'application/avro' })
publishAvro(): void {}
For runnable examples, see
sample/01-validation-class-validator
and
sample/02-validation-zod.