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.