npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

drizzle-zod-codegen

v0.2.0

Published

Generate static Zod schema files from Drizzle ORM schemas

Readme

drizzle-zod-codegen

Generate static Zod schema files from Drizzle ORM schemas.

Why?

Improved parity with drizzle-zod: Uses the same mapping logic and serializes most Zod runtime constructs ✅ Complete type coverage: All column types, enums, views ✅ Expanded Zod coverage: Many first-class Zod constructs are now supported (discriminated unions, intersections, pipelines, readonly, symbol, NaN, and static .catch() fallbacks). Native enums with numeric keys are preserved as object-literal enums.

  • Inspectable: Easy to see exactly what schemas you have
  • Version controllable: Track changes in git Extra Zod constructs supported Discriminated unions (z.discriminatedUnion) — emitted using z.discriminatedUnion when a discriminator is present Intersections (z.intersection) — emitted using z.intersection Pipelines and transforms (z.pipe) — emitted where possible as .pipe() when both sides are serializable Readonly objects (.readonly()) — emitted using .readonly() where applicable z.symbol(), z.nan() and object z.catch() fallbacks (static values only) are serializable
  • Better tree-shaking: Only import what you need

Serializer guarantees and behavior

  • Static defaults and fallbacks are serialized when they are deterministic values:
    • Example: .default('hello'), .catch('fallback') will be emitted as .default('hello') and .catch('fallback')
  • Dynamic factories used as defaults or catch fallbacks (e.g., .default(() => Math.random()), .catch(() => computeDefault())) cannot be serialized statically and will cause the generator to throw an error. Replace with a deterministic default or post-process the generated schema to integrate runtime factories.
  • For safety and to avoid silently permissive schemas, the serializer now throws for unsupported Zod constructs (for example, z.function()), rather than falling back to z.any(). This prevents subtle correctness issues and ensures generated schemas don't become lax silently.
  • Runtime-only refinements and predicates such as .refine(), .superRefine(), unsupported z.custom(...), and other effect-like closures are not serialized. The generator throws instead of silently weakening the schema.

Features

  • Unsupported Zod constructs or dynamic factories – If the generator throws with messages like Unsupported Zod type or ZodDefault uses a dynamic factory, this means your schema uses a runtime-only construct (for example, .default(() => Math.random()) or z.function()). Options:
    • Replace dynamic factories with static defaults or move the dynamic factory to runtime code and keep the generated schema static.
    • Post-process the generated .zod.ts to add the runtime-specific refinements or custom functions manually.
    • Write a small wrapper that extends the generated schema with .refine()/.transform() yourself.
  • Full parity with drizzle-zod: Uses the same mapping logic
  • All dialects supported: PostgreSQL, MySQL, SQLite, SingleStore
  • Complete type coverage: All column types, enums, views
  • Smart schema generation:
    • SelectSchema - for query results
    • InsertSchema - for inserts (excludes generated columns, makes defaults optional)
    • UpdateSchema - for updates (all fields optional)
  • Flexible discovery: Config file, directory scanning, or explicit paths
  • CLI and programmatic API

Installation

npm install -D drizzle-zod-codegen
# or
pnpm add -D drizzle-zod-codegen

Usage

CLI

# Generate from specific files
drizzle-zod-codegen generate src/schema.ts

# Scan a directory
drizzle-zod-codegen generate --dir src/db

# Use drizzle.config.ts/js/mts/mjs
drizzle-zod-codegen generate --config drizzle.config.ts

# Custom pattern
drizzle-zod-codegen generate --dir src --pattern "**/*.model.ts"

# Emit every schema into a single bundle file
drizzle-zod-codegen generate src/**/*.schema.ts --output single --out ./schemas/all.zod.ts

# Emit one file per Drizzle entity
drizzle-zod-codegen generate src/schema.ts --output per-schema --out-dir ./generated-schemas

# Load explicit custom-type mappings and per-entity refinements
drizzle-zod-codegen generate src/schema.ts --codegen-config ./drizzle-zod-codegen.config.ts

#### Additional Flags

- `--no-cache` – force `bundle-require` to rebundle each schema/config file instead of reusing a module cache entry, helpful when you run the generator repeatedly in the same process (watch mode or a scripted loop).

Output Modes

  • per-file (default) – writes one .zod file next to each schema (or mirrors into --out-dir).
  • per-schema – writes one file per table/view/enum named {Entity}.zod.ts. Combine with --out-dir to collect them.
  • single – aggregates every generated schema into the file provided via --out.

Programmatic API

import { generateZodSchemas, discoverSchemaFiles, runGenerateCommand } from 'drizzle-zod-codegen';

// Discover schema files
const schemaFiles = await discoverSchemaFiles({
  scanDir: 'src/db',
  pattern: '**/*.schema.ts',
});

// Generate for each file
for (const file of schemaFiles) {
  await generateZodSchemas({
    inputPath: file.path,
    outputPath: file.outputPath,
  });
}

// Or aggregate everything into a single file via runGenerateCommand
await runGenerateCommand({
	files: schemaFiles.map(file => file.path),
	mode: 'single',
	outFile: './all-schemas.zod.ts',
});

Pass forceRefreshModuleCache: true to generateZodSchemas() if you want to clear the loader cache before each invocation (useful for watch scripts that regenerate files without restarting the node process).

Custom SQL Types

drizzle-zod maps customType() columns to z.any() at runtime because Drizzle does not retain the TypeScript generic for arbitrary custom types at runtime. drizzle-zod-codegen now handles this in two explicit ways:

  • Built-in mapping for PostgreSQL citextz.string()
  • Optional customTypes config for any other SQL type name exposed by column.getSQLType()

Example config:

import { z } from 'zod';

export default {
  customTypes: {
    currency_code: z.string().length(3),
  },
  entities: {
    users: {
      insert: {
        // Any explicit drizzle-zod refinement tree still works here.
      },
    },
  },
};

When custom SQL types are mapped through this config, the generator uses drizzle-zod's own refinement hook so select/insert/update nullability and optionality still come from upstream logic.

TypeScript Support

Schema and config files are bundled through esbuild at runtime, so TypeScript enums, satisfies, const assertions, and other non-JavaScript syntax work without extra loaders.

Example

Given this Drizzle schema:

// users.schema.ts
import { pgTable, serial, text, integer, timestamp } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  age: integer('age'),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

Running drizzle-zod-codegen generate users.schema.ts generates:

// users.zod.ts
import { z } from 'zod';

export const UsersSelectSchema = z.object({
  id: z.number().int().gte(-2147483648).lte(2147483647),
  name: z.string(),
  email: z.string(),
  age: z.number().int().gte(-2147483648).lte(2147483647).nullable(),
  createdAt: z.date()
});

export const UsersInsertSchema = z.object({
  id: z.number().int().gte(-2147483648).lte(2147483647).optional(),
  name: z.string(),
  email: z.string(),
  age: z.number().int().gte(-2147483648).lte(2147483647).nullable().optional(),
  createdAt: z.date().optional()
});

export const UsersUpdateSchema = z.object({
  id: z.number().int().gte(-2147483648).lte(2147483647).optional(),
  name: z.string().optional(),
  email: z.string().optional(),
  age: z.number().int().gte(-2147483648).lte(2147483647).nullable().optional(),
  createdAt: z.date().optional()
});

Output Naming

  • Tables: {PascalCase}SelectSchema, {PascalCase}InsertSchema, {PascalCase}UpdateSchema
  • Views: {PascalCase}SelectSchema
  • Enums: {PascalCase}Schema

Supported Features

Column Types

  • All numeric types (with proper min/max validation)
  • Strings (with length constraints)
  • Dates & timestamps
  • Booleans
  • JSON & JSONB
  • Arrays
  • UUIDs
  • Enums
  • Geometry types (PostGIS)
  • Buffers
  • Custom SQL types via built-in mappings or --codegen-config

Schema Features

  • Primary keys
  • Not null constraints
  • Default values
  • Generated columns (excluded from insert/update)
  • Nullable columns
  • Views (including nested selections)
  • PostgreSQL enums

Discovery Options

1. Explicit Files (CLI)

drizzle-zod-codegen generate src/users.schema.ts src/posts.schema.ts

2. Directory Scanning

drizzle-zod-codegen generate --dir src/db --pattern "**/*.schema.ts"

Default pattern: **/*.schema.{ts,js,mts,mjs,cts,cjs}

3. Drizzle Config

// drizzle.config.ts
export default {
  schema: './src/db/schema.ts',
  // or multiple files
  schema: ['./src/db/users.ts', './src/db/posts.ts'],
  // or glob pattern
  schema: './src/db/**/*.schema.ts',
};

Then run:

drizzle-zod-codegen generate

Troubleshooting

  • Aggregate export errors – When serialization fails for a table, view, or enum export the generator throws an AggregateError; the CLI prints One or more exports failed to process: followed by each export's message so you can fix the offending export before retrying.
  • Schema cache misbehavior – Include the --no-cache flag when you run the generator repeatedly in the same process to force bundle-require to rebundle every schema/config file instead of reusing cached modules.

Output Files

By default, generates one .zod.ts file per schema file:

  • src/db/schema.tssrc/db/schema.zod.ts
  • users.schema.tsusers.zod.ts

Comparison with drizzle-zod

| Feature | drizzle-zod | drizzle-zod-codegen | |---------|------------|---------------------| | Schema generation | Runtime | Static files | | Type coverage | ✅ Complete | ✅ Complete | | All dialects | ✅ | ✅ | | Refinements | ✅ | ⏳ Planned | | Custom Zod instance | ✅ | ⏳ Planned | | Coercion | ✅ | ⏳ Planned | | File size | Small | Larger (explicit schemas) | | Inspectable | ❌ | ✅ | | Customizable output | ❌ | ✅ | | Build step required | ❌ | ✅ |

License

MIT

Credits

Built using the same column mapping logic as drizzle-zod to ensure 100% compatibility.