Skip to content

Events from Zod

document:docs/events/zod

Define an event once in TypeScript; the catalog, graph and service pages derive from it.

View as Markdown

The recommended path for TypeScript teams. You define the schema once in code, and the plugin converts it to JSON Schema and generates the event folder.

Terminal window
pnpm add -D autocatalog-plugin-zod
src/events/payment.ts
import { defineEvent } from 'autocatalog-plugin-zod';
import { z } from 'zod';
export default [
defineEvent(
{
domain: 'payments',
team: 'Billing',
producers: ['PaymentService'],
consumers: ['LedgerService', 'NotificationService'],
tags: ['pci', 'critical'],
},
z.object({
id: z.string(),
amount: z.number().positive(),
currency: z.string(),
completedAt: z.string().datetime(),
}),
),
];

defineEvent returns the Zod schema unchanged and attaches the metadata to a symbol. z.infer, .parse() and .safeParse() all keep working, so the same object stays your runtime validator.

Metadata can also carry delivery, what consumers may rely on about arrival; see Delivery.

Generate the event folder:

Terminal window
ac from-zod ./src/events/payment.ts --out ./events

That writes events/payments/PaymentCompleted/autocatalog.json with your metadata, schema.json converted from Zod, examples, and a generated index.mdx if you have not written narrative content yet.

Compact shape, with identity encoded in the name:

export default [
{
name: 'v1.payment.completed',
description: 'Fires when a payment completes',
schema: z.object({ id: z.string() }),
example: { id: 'pay_1' },
},
];

v1.payment.completed splits into version: v1, id: payment.completed, domain: payment.

Explicit shape — every field under your control:

export default [
{
id: 'payment.completed',
version: '1.0.0',
name: 'Payment Completed',
summary: 'Fires when a payment completes',
domain: 'payments',
producers: ['PaymentService'],
schema: z.object({ id: z.string() }),
examples: [{ name: 'success', data: { id: 'pay_1' } }],
},
];

Both shapes work with defineEvent. Where both carry the same field, the explicit one wins.

The catalog’s interchange format is JSON Schema, not Zod. Constraints that JSON Schema can express — types, requiredness, enums, formats, numeric bounds — survive. Refinements and transforms that only exist at runtime do not, and are out of scope for the catalog on purpose: the catalog is a contract other languages read, not a second validator.

ac scan can pick up Zod events as part of a whole-repository sweep, but extracting them means evaluating repository code. That is opt-in:

Terminal window
ac scan . --out ./events --allow-code-execution

Results merge by event id, and extracted Zod wins over inferred JSON Schema for the same id. See production operation for the isolation model that runtime plugins execute under.

Owned by MaintainersLast changed 4 Nov 2026SourceEdit this page