Sql To Zod
Schema Modeling & Type Safety

SQL CREATE TABLE DDL to Zod Schema Converter

Translate SQL table schemas, data types, constraints, and nullable columns into strongly-typed Zod object schemas.

Presets:
SQL to Zod Compiler Options & Type Rules
postgres•nullable•camel
Input(4 tables)
import { z } from 'zod';

// ============================================================================
// Enums
// ============================================================================

userRoleSchema = z.enum(['SUPERADMIN', 'ORG_ADMIN', 'MEMBER', 'BILLING', 'GUEST']);
UserRole = z.infer<typeof userRoleSchema>;

subscriptionStatusSchema = z.enum(['TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELED']);
SubscriptionStatus = z.infer<typeof subscriptionStatusSchema>;

// ============================================================================
// Table: organizations
// ============================================================================
organizationsSchema = z.object({
  id: z.string().uuid(),
  slug: z.string().max(64),
  name: z.string().max(255),
  billingEmail: z.string().email().max(255),
  status: subscriptionStatusSchema,
  settings: z.record(z.string(), z.unknown()).nullable(),
  isActive: z.boolean(),
  createdAt: z.coerce.date(),
  updatedAt: z.coerce.date(),
});

Organization = z.infer<typeof organizationsSchema>;

// ============================================================================
// Table: users
// ============================================================================
usersSchema = z.object({
  id: z.string().uuid(),
  organizationId: z.string().uuid(),
  email: z.string().email().max(255),
  fullName: z.string().max(120).nullable(),
  avatarUrl: z.string().url().max(500).nullable(),
  role: userRoleSchema,
  phoneNumber: z.string().max(32).nullable(),
  isVerified: z.boolean(),
  lastLoginAt: z.coerce.date().nullable(),
  createdAt: z.coerce.date(),
});

User = z.infer<typeof usersSchema>;

// ============================================================================
// Table: api_keys
// ============================================================================
apiKeysSchema = z.object({
  id: z.coerce.number().int().positive(),
  organizationId: z.string().uuid(),
  userId: z.string().uuid().nullable(),
  keyPrefix: z.string().max(12),
  hashedSecret: z.string().max(128),
  scopes: z.record(z.string(), z.unknown()),
  expiresAt: z.coerce.date().nullable(),
  revokedAt: z.coerce.date().nullable(),
  createdAt: z.coerce.date(),
});

ApiKey = z.infer<typeof apiKeysSchema>;

// ============================================================================
// Table: audit_logs
// ============================================================================
auditLogsSchema = z.object({
  id: z.coerce.number().int().positive(),
  organizationId: z.string().uuid(),
  actorId: z.string().uuid().nullable(),
  eventName: z.string().max(100),
  ipAddress: z.string().ip().nullable(),
  userAgent: z.string().max(500).nullable(),
  payload: z.record(z.string(), z.unknown()).nullable(),
  createdAt: z.coerce.date(),
});

AuditLog = z.infer<typeof auditLogsSchema>;
Parsed Tables4 schemas ready
Columns Mapped36 fields typed
Zod Schemas14 validators inferred

Zero-Knowledge Local Execution

All data processing, parsing, and compilation execute strictly inside your local browser memory. Zero external server uploads.

Sub-Millisecond AST Engine

All data payloads remain strictly local with zero external server communication.

Production Ready Export

Clean, optimized output formats ready for immediate deployment.

Technical Reference & Architecture Guide

SQL CREATE TABLE DDL to Zod Schema Converter — Reference Guide

Translate SQL table schemas, data types, constraints, and nullable columns into strongly-typed Zod object schemas.

Key Parameters & Controls

SQL DialectEnum (PostgreSQL | MySQL | SQLite | SQL Server | MariaDB)

Specifies the target SQL parser dialect for interpreting data types, column attributes (such as SERIAL, VARCHAR, DATETIME2, JSONB), identifier quoting, and constraint clauses.

Default: PostgreSQL
Nullability StrategyEnum (strict-nullable | nullish | optional-defaults)

Determines how SQL NULL and DEFAULT clauses translate into Zod modifiers: strict .nullable(), .nullish() (null or undefined), or .optional() for fields with column defaults or autoincrement keys.

Default: strict-nullable
Type Coercion ModeBoolean

Wraps primitive schema validations in z.coerce (e.g., z.coerce.number(), z.coerce.date(), z.coerce.boolean()) to safely cast incoming strings from form data and query params.

Default: false
Date Target FormatEnum (Date Instance (z.date()) | ISO String (z.string().datetime()) | Coerced Date (z.coerce.date()))

Defines how SQL TIMESTAMP, DATE, and DATETIME types are validated: as native JavaScript Date instances, ISO 8601 strings, or auto-coerced dates.

Default: Date Instance (z.date())
Generate Inferred TypesBoolean

Appends exported TypeScript type declarations derived via z.infer<typeof schema> for both table row outputs (select) and row inputs (insert).

Default: true

Pro Tips & Best Practices

In relational databases

In relational databases, primary keys with AUTO_INCREMENT or SERIAL and columns with DEFAULT expressions are generated automatically upon insertion. Define a base table schema and derive an insert schema using .omit({ id: true, createdAt: true }) or by chaining .optional() on generated columns.

SQL DDL typically types semi-structured

SQL DDL typically types semi-structured attributes simply as JSON or JSONB, which defaults to z.record(z.any()) or z.unknown(). Replace raw JSON types with dedicated nested Zod schemas to enforce strict contract boundaries between your database and frontend applications.

Standard JavaScript Number values exceed

Standard JavaScript Number values exceed safe integer bounds above 2^53 - 1. When converting SQL BIGINT or NUMERIC(p, s) / DECIMAL columns, map identifiers to z.bigint() or z.string() and monetary amounts to formatted string validations (e.g., z.string().regex(/^\d+\.\d{2}$/)) to avoid floating-point loss.

If your SQL database follows

If your SQL database follows snake_case conventions while your TypeScript codebase utilizes camelCase, use Zod transforms (.transform((data) => ({ ... }))) or schema pipelines (.pipe()) to normalize payload keys at runtime without modifying database schemas.

Common Gotchas & Troubleshooting

ZodError: Expected date, received string when parsing JSON payloads or API bodies

JSON payloads transmit dates as ISO 8601 strings, which fail direct z.date() validation. Switch the Date Target Format to ISO String (z.string().datetime()) or enable Type Coercion Mode (z.coerce.date()) to automatically parse strings into Date objects.

PostgreSQL ENUM types or custom domains trigger parsing errors in standalone DDL

Ensure custom PostgreSQL CREATE TYPE statements or inline column CHECK constraints are included within the input DDL. The converter will translate PostgreSQL enum definitions and CHECK (col IN (...)) clauses into idiomatic z.enum([...]) definitions.

Auto-increment primary keys fail validation during insert payload parsing

Primary key columns marked as SERIAL or IDENTITY are non-null in database schemas but omitted in client insert requests. Set the Nullability Strategy to 'optional-defaults' or derive an insert schema using .omit() to accommodate missing IDs during record creation.

Frequently Asked Questions

How does the converter map SQL column nullability to Zod schema modifiers?▼

Columns defined without NOT NULL are nullable in SQL. The converter marks these fields with .nullable(). When columns include a DEFAULT clause or autoincrement marker, you can configure the generator to output .optional() or .nullish() (which accepts both null and undefined) to mirror runtime insertion behavior.

What is the difference between z.date(), z.coerce.date(), and z.string().datetime()?▼

z.date() strictly checks that the input is an existing JavaScript Date object (standard for ORMs like Drizzle and Prisma). z.string().datetime() validates that an incoming string matches the ISO 8601 format without modifying it. z.coerce.date() accepts strings, unix epoch numbers, or Date objects and calls new Date(val) during validation.

Can generated Zod schemas be integrated with ORMs like Drizzle, Prisma, or Kysely?▼

Yes. The generated schemas serve as runtime validation contracts for API endpoints, Server Actions, or controller layers before data reaches your ORM or SQL query builder, guaranteeing type safety before database execution.

Is my SQL schema DDL transmitted to any external server or cloud service?▼

No. All SQL parsing, tokenization, AST generation, and TypeScript code synthesis execute 100% client-side inside your browser sandbox. No DDL statements, table names, or database structures are transmitted or persisted.

How are SQL CHECK constraints and FOREIGN KEY definitions handled?▼

SQL CHECK constraints specifying permitted values (e.g., status IN ('active', 'inactive')) are mapped to z.enum(). Numeric ranges (e.g., CHECK (age >= 18)) map to .min(18) refinements. Foreign key columns preserve their underlying scalar types (such as z.string().uuid() or z.number().int()).

Related Tools & Utilities

All Tools →