---
title: "Drizzle Zod Integration"
description: "Drizzle Zod Integration automatically generates modular Zod validation schemas directly from Drizzle ORM table and view definitions, bridging database schema declarations with runtime input validat..."
last_updated: "2026-10-12T08:14:00.811777+00:00"
canonical_url: "https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/ecosystem-packages/drizzle-zod-integration"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [drizzle-zod/src/schema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts)
- [drizzle-orm/type-tests/mysql/tables.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/type-tests/mysql/tables.ts)
- [drizzle-orm/type-tests/pg/tables.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/type-tests/pg/tables.ts)
- [drizzle-valibot/src/schema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-valibot/src/schema.ts)
- [drizzle-kit/src/snapshotsDiffer.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/snapshotsDiffer.ts)
- [drizzle-kit/src/serializer/pgSchema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/pgSchema.ts)
- [drizzle-arktype/src/schema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-arktype/src/schema.ts)
- [drizzle-typebox/src/schema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-typebox/src/schema.ts)
- [drizzle-kit/src/serializer/gelSchema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/gelSchema.ts)
- [drizzle-zod/src/schema.types.internal.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts)
- [drizzle-kit/src/introspect-pg.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/introspect-pg.ts)
- [drizzle-kit/src/introspect-gel.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/introspect-gel.ts)
- [drizzle-valibot/src/column.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-valibot/src/column.ts)
- [drizzle-kit/src/introspect-mysql.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/introspect-mysql.ts)
- [drizzle-zod/src/column.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts)
- [drizzle-zod/src/column.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.types.ts)
- [drizzle-typebox/src/schema.types.internal.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-typebox/src/schema.types.internal.ts)
- [drizzle-kit/src/introspect-singlestore.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/introspect-singlestore.ts)
- [drizzle-kit/src/serializer/sqliteSchema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/sqliteSchema.ts)
- [drizzle-valibot/src/schema.types.internal.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-valibot/src/schema.types.internal.ts)
- [drizzle-valibot/src/column.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-valibot/src/column.types.ts)
- [drizzle-kit/src/serializer/mysqlSchema.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/mysqlSchema.ts)
- [drizzle-arktype/src/schema.types.internal.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-arktype/src/schema.types.internal.ts)
- [drizzle-typebox/src/column.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-typebox/src/column.ts)
- [drizzle-zod/src/schema.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.ts)
- [drizzle-kit/src/serializer/studio.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-kit/src/serializer/studio.ts)
- [drizzle-valibot/src/schema.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-valibot/src/schema.types.ts)
- [drizzle-typebox/src/column.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-typebox/src/column.types.ts)
- [drizzle-typebox/src/schema.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-typebox/src/schema.types.ts)
- [drizzle-arktype/src/schema.types.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-arktype/src/schema.types.ts)
</details>

## Overview

Drizzle Zod Integration automatically generates modular Zod validation schemas directly from Drizzle ORM table and view definitions, bridging database schema declarations with runtime input validation. By analyzing column types, nullability constraints, default values, and generated identities, the integration produces precise Zod objects tailored for select, insert, and update operations. Custom schema factories allow developers to configure type coercion rules and custom Zod instances across operations.
Sources: [drizzle-zod/src/schema.ts:15-123](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L15-L123), [drizzle-zod/src/schema.types.ts:6-60](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.ts#L6-L60)

## Schema Generation Architecture

### Operation Condition Matrix

Public generator functions `createSelectSchema`, `createInsertSchema`, and `createUpdateSchema` analyze Drizzle ORM tables, views, and PostgreSQL enums to dynamically construct corresponding Zod validation schemas. The architecture relies on internal condition sets (`selectConditions`, `insertConditions`, and `updateConditions`) that dictate whether individual columns are omitted (`never`), marked optional (`optional`), or flagged as nullable (`nullable`) depending on the database operation.

Sources: [drizzle-zod/src/schema.ts:76-120](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.ts#L76-L120)

| Operation Rule Set | Omitted Columns (`never`) | Optional Columns (`optional`) | Nullable Columns (`nullable`) |
| :--- | :--- | :--- | :--- |
| `selectConditions` | `() => false` | `() => false` | `!column.notNull` |
| `insertConditions` | `generated.type === 'always' \|\| generatedIdentity.type === 'always'` | `!column.notNull \|\| (column.notNull && column.hasDefault)` | `!column.notNull` |
| `updateConditions` | `generated.type === 'always' \|\| generatedIdentity.type === 'always'` | `() => true` | `!column.notNull` |

Sources: [drizzle-zod/src/schema.ts:76-92](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L76-L92)

### Column and Entity Processing

The core schema builder resolves table and view columns via `getColumns`, which delegates to `getTableColumns` for table definitions and `getViewSelectedFields` for database views. Recursive execution handles nested relation structures and custom field groups.

```typescript
function getColumns(tableLike: Table | View) {
	return isTable(tableLike) ? getTableColumns(tableLike) : getViewSelectedFields(tableLike);
}

function handleColumns(
	columns: Record<string, any>,
	refinements: Record<string, any>,
	conditions: Conditions,
	factory?: CreateSchemaFactoryOptions<
		Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
	>,
): z.ZodType {
	const columnSchemas: Record<string, z.ZodType> = {};

	for (const [key, selected] of Object.entries(columns)) {
		if (!is(selected, Column) && !is(selected, SQL) && !is(selected, SQL.Aliased) && typeof selected === 'object') {
			const columns = isTable(selected) || isView(selected) ? getColumns(selected) : selected;
			columnSchemas[key] = handleColumns(columns, refinements[key] ?? {}, conditions, factory);
			continue;
		}
```
Sources: [drizzle-zod/src/schema.ts:15-34](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L15-L34)

> [!NOTE]
> When processing PostgreSQL enums via `createSelectSchema`, the generator bypasses column inspection entirely, resolving directly to `zod.enum(enum_.enumValues)` or using a custom `zodInstance` supplied through `createSchemaFactory`.
> Sources: [drizzle-zod/src/schema.ts:66-74](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L66-L74), [drizzle-zod/src/schema.ts:98-100](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L98-L100)

### Schema Factory Configuration

The `createSchemaFactory` function accepts options containing a custom `zodInstance` or global `coerce` rules, returning scoped instances of `createSelectSchema`, `createInsertSchema`, and `createUpdateSchema` bound to those configurations.

```typescript
export function createSchemaFactory<
	TCoerce extends Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined,
>(options?: CreateSchemaFactoryOptions<TCoerce>) {
	const createSelectSchema: CreateSelectSchema<TCoerce> = (
		entity: Table | View | PgEnum<[string, ...string[]]>,
		refine?: Record<string, any>,
	) => {
		if (isPgEnum(entity)) {
			return handleEnum(entity, options);
		}
		const columns = getColumns(entity);
		return handleColumns(columns, refine ?? {}, selectConditions, options) as any;
	};

	const createInsertSchema: CreateInsertSchema<TCoerce> = (
		entity: Table,
		refine?: Record<string, any>,
	) => {
		const columns = getColumns(entity);
		return handleColumns(columns, refine ?? {}, insertConditions, options) as any;
	};

	const createUpdateSchema: CreateUpdateSchema<TCoerce> = (
		entity: Table,
		refine?: Record<string, any>,
	) => {
		const columns = getColumns(entity);
		return handleColumns(columns, refine ?? {}, updateConditions, options) as any;
	};

	return { createSelectSchema, createInsertSchema, createUpdateSchema };
}
```
Sources: [drizzle-zod/src/schema.ts:121-152](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L121-L152)

> [!WARNING]
> Columns defined with `generatedalways` or `generatedIdentity` set to `'always'` evaluate to true under `conditions.never` for both insert and update operations, causing them to be entirely stripped from the resulting Zod object schemas.
> Sources: [drizzle-zod/src/schema.ts:83-83](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L83-L83), [drizzle-zod/src/schema.ts:89-89](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L89-L89)

## Column Conversion and Dialect Mapping

### Low-Level Column Mapping

The `columnToSchema` function inspects individual Drizzle column definitions to produce corresponding Zod type instances. For columns with defined enumerations, it constructs an enum schema using `z.enum(column.enumValues)`, falling back to a standard string schema if the list of values is empty.

```typescript
export function columnToSchema(
	column: Column,
	factory:
		| CreateSchemaFactoryOptions<
			Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
		>
		| undefined,
): zod.ZodType {
	const z: typeof zod = factory?.zodInstance ?? zod;
	const coerce = factory?.coerce ?? {};
	let schema!: zod.ZodType;

	if (isWithEnum(column)) {
		schema = column.enumValues.length ? z.enum(column.enumValues) : z.string();
	}

```
Sources: [drizzle-zod/src/column.ts:70-85](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L70-L85)

PostgreSQL geometric, vector, and line types receive specialized structural mapping. Tuple-based representations map to fixed-length Zod tuples, whereas object-based representations map to explicit Zod records with named coordinate keys.

```typescript
if (!schema) {
	// Handle specific types
	if (isColumnType<PgGeometry<any> | PgPointTuple<any>>(column, ['PgGeometry', 'PgPointTuple'])) {
		schema = z.tuple([z.number(), z.number()]);
	} else if (
		isColumnType<PgPointObject<any> | PgGeometryObject<any>>(column, ['PgGeometryObject', 'PgPointObject'])
	) {
		schema = z.object({ x: z.number(), y: z.number() });
	} else if (isColumnType<PgHalfVector<any> | PgVector<any>>(column, ['PgHalfVector', 'PgVector'])) {
		schema = z.array(z.number());
		schema = column.dimensions ? (schema as zod.ZodArray<any>).length(column.dimensions) : schema;
	} else if (isColumnType<PgLineTuple<any>>(column, ['PgLine'])) {
		schema = z.tuple([z.number(), z.number(), z.number()]);
	} else if (isColumnType<PgLineABC<any>>(column, ['PgLineABC'])) {
		schema = z.object({
			a: z.number(),
			b: z.number(),
			c: z.number(),
		});
	} // Handle other types
```
Sources: [drizzle-zod/src/column.ts:86-105](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L86-L105)

> [!NOTE]
> Vector and array columns inspect `column.dimensions` or `column.size` properties to apply exact length validation constraints via Zod array length modifiers.
> Sources: [drizzle-zod/src/column.ts:94-96](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L94-L96), [drizzle-zod/src/column.ts:106-108](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L106-L108)

### DataType and Coercion Handling

For standard data types, column mapping resolves primitive categories (`number`, `bigint`, `boolean`, `date`, `string`, `json`, `custom`, and `buffer`) and applies optional coercion settings defined via `createSchemaFactory`.

```typescript
	} else if (column.dataType === 'array') {
		schema = z.array(z.any());
	} else if (column.dataType === 'number') {
		schema = numberColumnToSchema(column, z, coerce);
	} else if (column.dataType === 'bigint') {
		schema = bigintColumnToSchema(column, z, coerce);
	} else if (column.dataType === 'boolean') {
		schema = coerce === true || coerce.boolean ? z.coerce.boolean() : z.boolean();
	} else if (column.dataType === 'date') {
		schema = coerce === true || coerce.date ? z.coerce.date() : z.date();
	} else if (column.dataType === 'string') {
		schema = stringColumnToSchema(column, z, coerce);
	} else if (column.dataType === 'json') {
		schema = jsonSchema;
	} else if (column.dataType === 'custom') {
		schema = z.any();
	} else if (column.dataType === 'buffer') {
		schema = bufferSchema;
	}
}
```
Sources: [drizzle-zod/src/column.ts:109-128](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L109-L128)

| Column DataType | Default Zod Mapping | Coercion Rule (`coerce.*` or `true`) |
| :--- | :--- | :--- |
| `number` | `numberColumnToSchema` | Handled internally by numeric range/integer logic |
| `bigint` | `bigintColumnToSchema` | Handled internally by bigint parsing rules |
| `boolean` | `z.boolean()` | `z.coerce.boolean()` |
| `date` | `z.date()` | `z.coerce.date()` |
| `string` | `stringColumnToSchema` | Handled internally by string configuration |
| `json` | `jsonSchema` | Unaffected by scalar coercion |
| `custom` | `z.any()` | Unaffected by scalar coercion |
| `buffer` | `bufferSchema` | Unaffected by scalar coercion |

Sources: [drizzle-zod/src/column.ts:111-127](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L111-L127)

> [!WARNING]
> If a column datatype matches none of the recognized branches and has no associated enum structure, `columnToSchema` defaults the resulting validation target to `z.any()`.
> Sources: [drizzle-zod/src/column.ts:130-132](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L130-L132)

### Enum Normalization and SQL Translation

PostgreSQL enums processed outside table definitions via `handleEnum` extract enum values directly from `enum_.enumValues` using the active Zod instance.

```typescript
function handleEnum(
	enum_: PgEnum<any>,
	factory?: CreateSchemaFactoryOptions<
		Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
	>,
) {
	const zod: typeof z = factory?.zodInstance ?? z;
	return zod.enum(enum_.enumValues);
}
```
Sources: [drizzle-zod/src/schema.ts:66-74](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L66-L74)

Numeric columns inspect their SQL type definitions through `column.getSQLType()` to detect unsigned flags, establishing strict min and max boundaries corresponding to database integer widths.

```typescript
function numberColumnToSchema(
	column: Column,
	z: typeof zod,
	coerce: CreateSchemaFactoryOptions<
		Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
	>['coerce'],
): zod.ZodType {
	let unsigned = column.getSQLType().includes('unsigned');
	let min!: number;
	let max!: number;
	let integer = false;

	if (isColumnType<MySqlTinyInt<any> | SingleStoreTinyInt<any>>(column, ['MySqlTinyInt', 'SingleStoreTinyInt'])) {
		min = unsigned ? 0 : CONSTANTS.INT8_MIN;
		max = unsigned ? CONSTANTS.INT8_UNSIGNED_MAX : CONSTANTS.INT8_MAX;
		integer = true;
```
Sources: [drizzle-zod/src/column.ts:137-152](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L137-L152)

## Type Inference and Internal Constraints

### Compile-Time Schema Types and Utility Helpers

The compilation phase orchestrates how database tables, columns, and relations are mapped into strongly-typed Zod object schemas via recursive helper types. The core builder utility `BuildSchema` accepts an operation mode (`'select' | 'insert' | 'update'`), a record of columns, refinement configurations, and coercion rules. It iterates through table keys, applying generated-column filters where generated-always-as columns are restricted to select schemas while remaining present or omitted across other operation types.

```typescript
export type BuildSchema<
	TType extends 'select' | 'insert' | 'update',
	TColumns extends Record<string, any>,
	TRefinements extends Record<string, any> | undefined,
	TCoerce extends Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined,
> = z.ZodObject<
	Simplify<
		{
			[
				K in keyof TColumns as ColumnIsGeneratedAlwaysAs<TColumns[K]> extends true ? TType extends 'select' ? K
					: never
					: K
			]: TColumns[K] extends infer TColumn extends Column
				? IsRefinementDefined<TRefinements, K> extends true
					? Assume<HandleRefinement<TType, TRefinements[K & keyof TRefinements], TColumn>, z.ZodType>
				: HandleColumn<TType, TColumn, TCoerce>
				: TColumns[K] extends infer TObject extends SelectedFieldsFlat<Column> | Table | View ? BuildSchema<
						TType,
						GetSelection<TObject>,
						TRefinements extends object ? TRefinements[K & keyof TRefinements] : undefined,
						TCoerce
					>
				: z.ZodAny;
		}
	>,
	{ out: {}; in: {} }
>;
```
Sources: [drizzle-zod/src/schema.types.internal.ts:40-66](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L40-L66)

The framework evaluates column nullability and default states through conditional wrapper types like `HandleSelectColumn`, `HandleInsertColumn`, and `HandleUpdateColumn`. Required select fields retain their underlying Zod schema types, whereas nullable columns wrap them in `z.ZodNullable`. Insert and update helpers inject `z.ZodOptional` based on whether columns possess default values or allow null assignment.

```typescript
type HandleSelectColumn<
	TSchema extends z.ZodType,
	TColumn extends Column,
> = TColumn['_']['notNull'] extends true ? TSchema
	: z.ZodNullable<TSchema>;

type HandleInsertColumn<
	TSchema extends z.ZodType,
	TColumn extends Column,
> = TColumn['_']['notNull'] extends true ? TColumn['_']['hasDefault'] extends true ? z.ZodOptional<TSchema>
	: TSchema
	: z.ZodOptional<z.ZodNullable<TSchema>>;

type HandleUpdateColumn<
	TSchema extends z.ZodType,
	TColumn extends Column,
> = TColumn['_']['notNull'] extends true ? z.ZodOptional<TSchema>
	: z.ZodOptional<z.ZodNullable<TSchema>>;

```
Sources: [drizzle-zod/src/column.types.ts:76-94](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L76-L94)

| Operation Mode | Nullable & No Default | Not-Null & Has Default | Not-Null & No Default |
| :--- | :--- | :--- | :--- |
| `select` | `z.ZodNullable<TSchema>` | `TSchema` | `TSchema` |
| `insert` | `z.ZodOptional<z.ZodNullable<TSchema>>` | `z.ZodOptional<TSchema>` | `TSchema` |
| `update` | `z.ZodOptional<z.ZodNullable<TSchema>>` | `z.ZodOptional<TSchema>` | `z.ZodOptional<TSchema>` |

Sources: [drizzle-zod/src/column.types.ts:76-94](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.types.ts#L76-L94)

> [!NOTE]
> Selection fields supporting nested relations or embedded objects recursively invoke `BuildSchema` against sub-selections derived from `GetSelection<TObject>`.
> Sources: [drizzle-zod/src/schema.types.internal.ts:56-61](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L56-L61)

### Guardrails and DrizzleTypeError Protection

To prevent invalid schema configurations at compile time, internal type assertions use `DrizzleTypeError` guardrails. When schema refinements introduce keys that do not exist on the target comparison type, `NoUnknownKeys` intercepts the invalid property and triggers a descriptive compile-time failure.

```typescript
export type NoUnknownKeys<
	TRefinement extends Record<string, any>,
	TCompare extends Record<string, any>,
> = {
	[K in keyof TRefinement]: K extends keyof TCompare
		? TRefinement[K] extends Record<string, z.ZodType> ? NoUnknownKeys<TRefinement[K], TCompare[K]>
		: TRefinement[K]
		: DrizzleTypeError<`Found unknown key in refinement: "${K & string}"`>;
};
```
Sources: [drizzle-zod/src/schema.types.internal.ts:68-76](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L68-L76)

Type coercion validation relies on the `CanCoerce` helper, which checks whether global coercion is enabled (`true`) or if specific primitive flags within the partial coercion record are marked as true for types like `bigint`, `boolean`, `date`, `number`, or `string`.

```typescript
type CanCoerce<
	TCoerce extends Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined,
	TTo extends 'bigint' | 'boolean' | 'date' | 'number' | 'string',
> = TCoerce extends true ? true
	: TCoerce extends Record<string, any> ? TCoerce[TTo] extends true ? true
		: false
	: false;
```
Sources: [drizzle-zod/src/column.types.ts:36-42](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/column.ts#L36-L42)

> [!WARNING]
> Passing unregistered keys inside refinement objects causes `NoUnknownKeys` to evaluate the property against `DrizzleTypeError`, halting compilation with an explicit error message naming the offending key.
> Sources: [drizzle-zod/src/schema.types.internal.ts:68-76](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L68-L76)

## Custom Overrides and Refinement Pipelines

### Column Refinements and Direct Replacements

The schema generation pipeline accepts an optional refinement record as its second parameter, allowing developers to customize individual column validation rules or completely replace generated Zod types. During schema building in `handleColumns`, the generator checks whether a refinement is defined for a given column key. If the refinement value is provided and is not a function, it directly overrides the column schema. If the refinement is defined as a function, it is invoked with the automatically generated column schema as its argument, enabling chaining or custom validation transformations.

```typescript
function handleColumns(
	columns: Record<string, any>,
	refinements: Record<string, any>,
	conditions: Conditions,
	factory?: CreateSchemaFactoryOptions<
		Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
	>,
): z.ZodType {
	const columnSchemas: Record<string, z.ZodType> = {};

	for (const [key, selected] of Object.entries(columns)) {
		if (!is(selected, Column) && !is(selected, SQL) && !is(selected, SQL.Aliased) && typeof selected === 'object') {
			const columns = isTable(selected) || isView(selected) ? getColumns(selected) : selected;
			columnSchemas[key] = handleColumns(columns, refinements[key] ?? {}, conditions, factory);
			continue;
		}

		const refinement = refinements[key];
		if (refinement !== undefined && typeof refinement !== 'function') {
			columnSchemas[key] = refinement;
			continue;
		}

		const column = is(selected, Column) ? selected : undefined;
		const schema = column ? columnToSchema(column, factory) : z.any();
		const refined = typeof refinement === 'function' ? refinement(schema) : schema;

```
Sources: [drizzle-zod/src/schema.ts:19-45](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L19-L45)

```typescript
const userInsertSchema = createInsertSchema(users, {
  email: (schema) => schema.email(),
  age: z.number().min(18),
});
```
Sources: [drizzle-zod/src/schema.ts:105-111](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L105-L111)

> [!NOTE]
> Refinement functions receive the exact base schema generated by `columnToSchema`, preserving nullability and optional wrapping unless explicitly overridden inside the transform callback.
> Sources: [drizzle-zod/src/schema.ts:42-45](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L42-L45)

### Nested Structures and View Field Selections

When processing tables, views, or relational selections, `handleColumns` inspects entry values recursively. If an entry key represents a nested object or view rather than a primitive column or SQL expression, the generator retrieves the underlying column map via `getColumns`—which evaluates `getTableColumns` for tables and `getViewSelectedFields` for database views—and processes the nested record recursively using the matching refinement subsection.

```typescript
function getColumns(tableLike: Table | View) {
	return isTable(tableLike) ? getTableColumns(tableLike) : getViewSelectedFields(tableLike);
}

function handleColumns(
	columns: Record<string, any>,
	refinements: Record<string, any>,
	conditions: Conditions,
	factory?: CreateSchemaFactoryOptions<
		Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined
	>,
): z.ZodType {
	const columnSchemas: Record<string, z.ZodType> = {};

	for (const [key, selected] of Object.entries(columns)) {
		if (!is(selected, Column) && !is(selected, SQL) && !is(selected, SQL.Aliased) && typeof selected === 'object') {
			const columns = isTable(selected) || isView(selected) ? getColumns(selected) : selected;
			columnSchemas[key] = handleColumns(columns, refinements[key] ?? {}, conditions, factory);
			continue;
		}
```
Sources: [drizzle-zod/src/schema.ts:15-34](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L15-L34)

Type checking for these nested refinement structures is enforced at compile time through recursive conditional typing. The `BuildRefine` type utility maps over columns and nested relations, validating that any provided refinement matches either a valid Zod type or a transformation function accepting the corresponding column type schema.

```typescript
export type BuildRefine<
	TColumns extends Record<string, any>,
	TCoerce extends Partial<Record<'bigint' | 'boolean' | 'date' | 'number' | 'string', true>> | true | undefined,
> = {
	[K in keyof TColumns as TColumns[K] extends Column | SelectedFieldsFlat<Column> | Table | View ? K : never]?:
		TColumns[K] extends Column ? BuildRefineField<GetZodType<TColumns[K], TCoerce>>
			: BuildRefine<GetSelection<TColumns[K]>, TCoerce>;
};
```
Sources: [drizzle-zod/src/schema.types.internal.ts:14-21](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L14-L21)

> [!WARNING]
> Supplying an unknown key inside a nested refinement tree trips the `NoUnknownKeys` helper, halting type compilation with a `DrizzleTypeError` indicating the illegal property name.
> Sources: [drizzle-zod/src/schema.types.internal.ts:68-76](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.types.internal.ts#L68-L76)

## Form Handling and Runtime Validation

### Input Mutation Parsing and Mutation Validation

Generated Zod schemas integrate into application form handling and API request validation pipelines by defining runtime parsers for database mutations. Using `createInsertSchema` and `createUpdateSchema`, applications validate incoming payloads before executing database operations. The condition rules built into `insertConditions` and `updateConditions` automatically omit database-generated columns—such as columns configured with `always` generation or identity defaults—while enforcing required fields, nullability, and optional defaults for client-supplied payloads.

```typescript
const insertConditions: Conditions = {
	never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
	optional: (column) => !column.notNull || (column.notNull && column.hasDefault),
	nullable: (column) => !column.notNull,
};

const updateConditions: Conditions = {
	never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
	optional: () => true,
	nullable: (column) => !column.notNull,
};

```
Sources: [drizzle-zod/src/schema.ts:82-93](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L82-L93)

```typescript
const insertUserSchema = createInsertSchema(users);
const validatedData = insertUserSchema.parse(requestBody);
```
Sources: [drizzle-zod/src/schema.ts:105-111](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L105-L111)

> [!CAUTION]
> Columns defined with `generatedAlwaysAs` or `always` identity are completely excluded from insert and update schemas via `conditions.never`, causing Zod to strip these fields if submitted in client mutation payloads.
> Sources: [drizzle-zod/src/schema.ts:83-89](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L83-L89)

### Query Result and View Validation

For read operations and user interface rendering, `createSelectSchema` generates runtime validation contracts that reflect exact table structures and database views. Unlike insertion handlers, select schemas evaluate columns against `selectConditions`, preserving all table fields while mapping nullable database columns to Zod nullable wrappers without marking them optional. This ensures API responses and UI form initializers match the exact shape queried from the database.

```typescript
const selectConditions: Conditions = {
	never: () => false,
	optional: () => false,
	nullable: (column) => !column.notNull,
};
```
Sources: [drizzle-zod/src/schema.ts:76-80](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L76-L80)

```typescript
const selectUserSchema = createSelectSchema(users);
const userProfile = selectUserSchema.parse(dbRecord);
```
Sources: [drizzle-zod/src/schema.ts:94-103](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L94-L103)

> [!NOTE]
> When passed a database view or an enumerated type directly, `createSelectSchema` resolves view selected fields via `getViewSelectedFields` or builds a Zod enum parser from `enumValues`.
> Sources: [drizzle-zod/src/schema.ts:15-17](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L15-L17), [drizzle-zod/src/schema.ts:98-100](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-zod/src/schema.ts#L98-L100)

## Related

- [Table and Column Models](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/table-and-column-models)
- [Drizzle TypeBox Integration](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/ecosystem-packages/drizzle-typebox-integration)
- [Drizzle Valibot Integration](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/ecosystem-packages/drizzle-valibot-integration)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/llms.txt) for all pages in this wiki.
