
zod-to-d2 is a TypeScript library that extends the Zod schema validation library, enabling you to annotate your schemas and automatically generate D2 diagrams. This helps visualize data models, relationships, and schema structures directly from your Zod definitions.
Features
- Schema Annotation: Add metadata (primary keys, foreign keys, notes, table names) to Zod schemas.
- Automatic Diagram Generation: Convert annotated Zod schemas into D2 diagrams.
- Relationship Mapping: Visualize foreign keys, primary keys, and other relationships.
- Extensible: Supports custom extensions for notes, table names, and more.
- CLI Tool: Generate diagrams from files or directories via command line.
- TypeScript Support: Fully typed for safe and predictable usage.
Installation
pnpm add @eng-tools/zod-to-d2
# or
npm install @eng-tools/zod-to-d2
# or
yarn add @eng-tools/zod-to-d2Usage
- Annotate your Zod schemas
import { z } from "zod";
import "@eng-tools/zod-to-d2";
export const userSchema = z
.object({
id: z.string().primaryKey(), //<-- marks the 'id' property as a PK
name: z.string().notes("The name of the user"), //<-- includes comments on this property on the output diagram
email: z.string(),
})
.tableName("users"); //<-- give the table/entity a name
export const postSchema = z
.object({
id: z.string().primaryKey(),
authorId: z.string().foreignKey(userSchema, "id"), //<- creates a FK relationship to the 'id' property of the 'userSchema'
content: z.string(),
})
.tableName("posts"); //<-- give the table/entity a name- Generate a D2 diagram (programmatic API)
import { generateDiagramText } from "@eng-tools/zod-to-d2";
const diagram = generateDiagramText([userSchema, postSchema]);
console.log(diagram); // D2 diagram sourceThis outputs the following D2 text:
direction: down
title: |md Sample Diagram | {near: top-center}
users: {
shape: sql_table
"id": string {constraint: [primary_key]}
"name": string # The name of the user
"email": string
}
posts: {
shape: sql_table
"id": string {constraint: [primary_key]}
"authorId": string {constraint: [foreign_key]}
"content": string
}
posts.authorId <-> users.id: {
source-arrowhead: {shape: cf-many}
target-arrowhead: {shape: cf-one-required}
}You can find how this looks on the D2 Playground here, or as shown in the image below.

- Generate a D2 diagram (CLI)
> pnpm zod2d2 --file-paths src/schemas/user.ts src/schemas/post.ts --output-path diagram.d2 --title "My Diagram"
# or
> pnpm zod2d2 --directory src/schemas --output-path diagram.d2