Skip to main content

TypeScript & Zod Schema Generator

TalkingSchema's AI copilot generates TypeScript interfaces, Zod validation schemas, and runtime type guards from any ERD. Column types, nullability, enums, check constraints, and default values are all encoded into accurate TypeScript and Zod output — eliminating the manual labor of keeping your database schema and API validation layer in sync.

Outputs are provided as TypeScript code blocks, with an option to download a .ts file coming soon


What Gets Generated

For each table in your ERD, TalkingSchema can produce:

Output typeDescription
TypeScript interface (select)Reflects exactly what the database returns — all columns, nullable where the column is nullable
TypeScript interface (insert)Omits auto-generated fields (id, created_at); marks optional fields with ?
Zod schema (insert)Runtime validation schema for API request bodies — use with Express, Fastify, Hono, tRPC
Zod schema (partial update).partial() version for PATCH endpoints
Zod enumMaps database enum types to z.enum([...])
Type guardisX(value: unknown): value is X function for runtime narrowing

How to Generate

Zod schemas for all tables

Generate Zod validation schemas for every table in this ERD.
Include: insert schema (for POST body validation), select type
(TypeScript interface), and partial schema (for PATCH endpoints).
Export each as named exports.

TypeScript interfaces only

Generate TypeScript interfaces for each table.
- SelectType: all columns, nullable where column is nullable
- InsertType: exclude auto-generated columns (id, created_at, updated_at)
- UpdateType: all non-pk columns as optional

Example Output

Using the Global Sustainable Supply Chain (GSSC) schema:

// types/suppliers.ts
import { z } from "zod";

// --- Enums ---
export const CarbonTierEnum = z.enum(["A", "B", "C", "D"]);
export type CarbonTier = z.infer<typeof CarbonTierEnum>;

// --- Select type (what the database returns) ---
export interface SupplierSelect {
supplier_id: string; // UUID
company_name: string;
country: string;
carbon_tier: CarbonTier;
certification: string | null;
is_active: boolean;
created_at: Date;
updated_at: Date;
}

// --- Zod insert schema (for POST /suppliers request body) ---
export const SupplierInsertSchema = z.object({
company_name: z.string().min(1).max(200),
country: z.string().min(1).max(100),
carbon_tier: CarbonTierEnum,
certification: z.string().max(100).nullable().optional(),
is_active: z.boolean().optional().default(true),
});
export type SupplierInsert = z.infer<typeof SupplierInsertSchema>;

// --- Zod partial schema (for PATCH /suppliers/:id request body) ---
export const SupplierUpdateSchema = SupplierInsertSchema.partial();
export type SupplierUpdate = z.infer<typeof SupplierUpdateSchema>;

// types/products.ts
import { z } from "zod";
import { CarbonTierEnum } from "./suppliers";

export interface ProductSelect {
product_id: string;
sku: string;
name: string;
category: string;
unit_weight_kg: number | null; // DECIMAL maps to number
unit_cost_usd: number;
carbon_intensity_score: number | null;
supplier_id: string;
is_active: boolean;
created_at: Date;
updated_at: Date;
}

export const ProductInsertSchema = z.object({
sku: z.string().min(1).max(50),
name: z.string().min(1).max(200),
category: z.string().min(1).max(100),
unit_weight_kg: z.number().positive().nullable().optional(),
unit_cost_usd: z.number().nonnegative(),
carbon_intensity_score: z.number().min(0).max(999.99).nullable().optional(),
supplier_id: z.string().uuid(),
is_active: z.boolean().optional().default(true),
});
export type ProductInsert = z.infer<typeof ProductInsertSchema>;
export const ProductUpdateSchema = ProductInsertSchema.partial();

// types/orders.ts
import { z } from "zod";

export const OrderStatusEnum = z.enum([
"draft",
"confirmed",
"shipped",
"received",
"cancelled",
]);
export type OrderStatus = z.infer<typeof OrderStatusEnum>;

export const PurchaseOrderInsertSchema = z.object({
supplier_id: z.string().uuid(),
warehouse_id: z.string().uuid(),
order_date: z.coerce.date(),
expected_delivery: z.coerce.date().nullable().optional(),
status: OrderStatusEnum.optional().default("draft"),
});
export type PurchaseOrderInsert = z.infer<typeof PurchaseOrderInsertSchema>;

export const PurchaseOrderItemInsertSchema = z.object({
po_id: z.string().uuid(),
product_id: z.string().uuid(),
quantity: z.number().int().positive(),
unit_cost_usd: z.number().nonnegative(),
});
export type PurchaseOrderItemInsert = z.infer<
typeof PurchaseOrderItemInsertSchema
>;

Customizing the Output

RequirementPrompt addition
camelCase field names"Use camelCase for all TypeScript property names"
valibot instead of Zod"Use valibot syntax instead of Zod"
Include discriminated union for status"Generate a discriminated union type for order status transitions"
API response wrapper"Wrap each select type in a generic ApiResponse<T> wrapper"
Strict decimal handling"Use string instead of number for DECIMAL columns to avoid floating-point precision loss"
Separate file per table"Output each table's types and schemas in its own file"

Frequently Asked Questions

How are DECIMAL columns handled in TypeScript?

By default, TalkingSchema maps DECIMAL/NUMERIC columns to TypeScript number. If you are handling monetary values or require arbitrary precision, ask the AI to use string instead: "Map all DECIMAL columns to string in TypeScript to avoid floating-point precision issues."

How are enums handled?

Database enum types are mapped to z.enum([...]) with the exact values defined in the schema. The inferred TypeScript type is exported as a union.

Can I get Zod schemas compatible with tRPC?

Yes. Ask: "Generate Zod input schemas formatted for use as tRPC procedure inputs. Use the insert schema for mutations and the UUID schema for queries by ID."

Does this replace a code generator like zod-prisma?

TalkingSchema generates Zod schemas from your ERD model directly — it is not a Prisma plugin. It is most useful for teams not using Prisma, or for generating portable validation schemas that are independent of any ORM.