Skip to main content

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.