Generate static Zod schema files from Drizzle ORM schemas
npm install drizzle-zod-codegenGenerate static Zod schema files from Drizzle ORM schemas.
drizzle-zod creates Zod schemas at runtime, which is great for many use cases. However, static generated files offer several advantages:
- Inspectable: Easy to see exactly what schemas you have
- Version controllable: Track changes in git
- No runtime overhead: Schemas are pre-generated
- Better tree-shaking: Only import what you need
- Easier to customize: Modify generated files if needed
- ✅ 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
``bash`
npm install -D drizzle-zod-codegenor
pnpm add -D drizzle-zod-codegen
`bashGenerate from specific files
drizzle-zod-codegen generate src/schema.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).`
- 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.
`typescript
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).
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.
Given this Drizzle schema:
`typescript
// 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:
`typescript
// 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()
});
`
- Tables: {PascalCase}SelectSchema, {PascalCase}InsertSchema, {PascalCase}UpdateSchema{PascalCase}SelectSchema
- Views: {PascalCase}Schema
- Enums:
bash
drizzle-zod-codegen generate src/users.schema.ts src/posts.schema.ts
`$3
`bash
drizzle-zod-codegen generate --dir src/db --pattern "*/.schema.ts"
`Default pattern:
*/.schema.{ts,js,mts,mjs,cts,cjs}$3
`typescript
// 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:
`bash
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.ts → src/db/schema.zod.ts
- users.schema.ts → users.zod.ts`| 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 | ❌ | ✅ |
MIT
Built using the same column mapping logic as drizzle-zod to ensure 100% compatibility.