Relevant source files
The following files were used as context for generating this wiki page:
- drizzle-arktype/src/column.ts
- drizzle-orm/type-tests/mysql/tables.ts
- drizzle-arktype/src/schema.ts
- drizzle-orm/type-tests/singlestore/tables.ts
- drizzle-kit/src/serializer/gelSchema.ts
- drizzle-kit/src/introspect-mysql.ts
- drizzle-arktype/src/schema.types.internal.ts
- drizzle-kit/src/introspect-pg.ts
- drizzle-kit/src/serializer/pgSchema.ts
- drizzle-arktype/src/index.ts
- drizzle-kit/src/introspect-singlestore.ts
- drizzle-kit/src/introspect-gel.ts
- drizzle-arktype/src/column.types.ts
- drizzle-kit/src/serializer/mysqlSchema.ts
- drizzle-valibot/src/column.ts
- drizzle-kit/src/serializer/pgSerializer.ts
- drizzle-arktype/src/utils.ts
- drizzle-valibot/src/column.types.ts
- drizzle-valibot/src/schema.ts
- drizzle-typebox/src/column.ts
- drizzle-kit/src/schemaValidator.ts
- drizzle-kit/src/serializer/sqliteSchema.ts
- drizzle-arktype/src/schema.types.ts
- drizzle-seed/src/types/drizzleStudio.ts
- drizzle-kit/src/serializer/studio.ts
- integration-tests/drizzle2/pg/meta/0000_snapshot.json
- drizzle-kit/src/serializer/singlestoreSchema.ts
- drizzle-seed/src/types/tables.ts
- drizzle-typebox/src/schema.types.internal.ts
Overview
Drizzle ArkType Integration bridges Drizzle ORM definitions with the ArkType validation library by automatically generating runtime type validators and static TypeScript types from database schemas. Operating across tables, views, and enums, it translates column primitives, constraints, and relational selections into validated ArkType structures, powering type-safe data validation and insertions.
Sources: drizzle-arktype/src/column.ts:66-126, drizzle-arktype/src/schema.ts:61-98, drizzle-arktype/src/schema.types.ts:6-48
Public Schema Generation API
Schema Factory Functions
The public API provides three main factory functions — createSelectSchema, createInsertSchema, and createUpdateSchema — to generate ArkType validation schemas from Drizzle table, view, and enum definitions. Each factory inspects the entity columns or fields, applies custom field refinements, and enforces nullability and optionality rules according to the operation type.
export const createSelectSchema = ((
entity: Table | View | PgEnum<[string, ...string[]]>,
refine?: Record<string, any>,
) => {
if (isPgEnum(entity)) {
return type.enumerated(...entity.enumValues);
}
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: () => false,
optional: () => false,
nullable: (column) => !column.notNull,
}) as any;
}) as CreateSelectSchema;
export const createInsertSchema = ((
entity: Table,
refine?: Record<string, any>,
) => {
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
optional: (column) => !column.notNull || (column.notNull && column.hasDefault),
nullable: (column) => !column.notNull,
}) as any;
}) as CreateInsertSchema;
export const createUpdateSchema = ((
entity: Table,
refine?: Record<string, any>,
) => {
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
optional: () => true,
nullable: (column) => !column.notNull,
}) as any;
}) as CreateUpdateSchema;Sources: drizzle-arktype/src/schema.ts:61-98
The behavior of each factory function depends on specific condition evaluators configured during column iteration:
| Function | Target Entity | never Condition | optional Condition | nullable Condition |
|---|---|---|---|---|
createSelectSchema | Table, View, PgEnum | Always false | Always false | !column.notNull |
createInsertSchema | Table | generated === 'always' or generatedIdentity === 'always' | `!column.notNull | |
createUpdateSchema | Table | generated === 'always' or generatedIdentity === 'always' | Always true | !column.notNull |
export const createSelectSchema = ((
entity: Table | View | PgEnum<[string, ...string[]]>,
refine?: Record<string, any>,
) => {
if (isPgEnum(entity)) {
return type.enumerated(...entity.enumValues);
}
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: () => false,
optional: () => false,
nullable: (column) => !column.notNull,
}) as any;
}) as CreateSelectSchema;
export const createInsertSchema = ((
entity: Table,
refine?: Record<string, any>,
) => {
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
optional: (column) => !column.notNull || (column.notNull && column.hasDefault),
nullable: (column) => !column.notNull,
}) as any;
}) as CreateInsertSchema;
export const createUpdateSchema = ((
entity: Table,
refine?: Record<string, any>,
) => {
const columns = getColumns(entity);
return handleColumns(columns, refine ?? {}, {
never: (column) => column?.generated?.type === 'always' || column?.generatedIdentity?.type === 'always',
optional: () => true,
nullable: (column) => !column.notNull,
}) as any;
}) as CreateUpdateSchema;Sources: drizzle-arktype/src/schema.ts:61-98
Note
When createSelectSchema receives a PgEnum, it bypasses column iteration entirely and immediately constructs an ArkType enumerated validator using the enum's enumValues.
Sources: drizzle-arktype/src/schema.ts:65-67
Overloads and Refinement Signatures
The TypeScript interfaces for createSelectSchema, createInsertSchema, and createUpdateSchema support overloaded signatures for tables and views, as well as optional refinement objects to customize field validators.
export interface CreateSelectSchema {
<TTable extends Table>(table: TTable): BuildSchema<'select', TTable['_']['columns'], undefined>;
<
TTable extends Table,
TRefine extends BuildRefine<TTable['_']['columns']>,
>(
table: TTable,
refine?: NoUnknownKeys<TRefine, TTable['$inferSelect']>,
): BuildSchema<'select', TTable['_']['columns'], TRefine>;
<TView extends View>(view: TView): BuildSchema<'select', TView['_']['selectedFields'], undefined>;
<
TView extends View,
TRefine extends BuildRefine<TView['_']['selectedFields']>,
>(
view: TView,
refine: NoUnknownKeys<TRefine, TView['$inferSelect']>,
): BuildSchema<'select', TView['_']['selectedFields'], TRefine>;
<TEnum extends PgEnum<any>>(enum_: TEnum): Type<TEnum['enumValues'][number]>;
}
export interface CreateInsertSchema {
<TTable extends Table>(table: TTable): BuildSchema<'insert', TTable['_']['columns'], undefined>;
<
TTable extends Table,
TRefine extends BuildRefine<Pick<TTable['_']['columns'], keyof TTable['$inferInsert']>>,
>(
table: TTable,
refine?: NoUnknownKeys<TRefine, TTable['$inferInsert']>,
): BuildSchema<'insert', TTable['_']['columns'], TRefine>;
}
export interface CreateUpdateSchema {
<TTable extends Table>(table: TTable): BuildSchema<'update', TTable['_']['columns'], undefined>;
<
TTable extends Table,
TRefine extends BuildRefine<Pick<TTable['_']['columns'], keyof TTable['$inferInsert']>>,
>(
table: TTable,
refine?: TRefine,
): BuildSchema<'update', TTable['_']['columns'], TRefine>;
}Sources: drizzle-arktype/src/schema.types.ts:6-48
Important
For insert and update schemas, refinement typings are constrained via NoUnknownKeys to keys present in $inferInsert, preventing developers from specifying refinements for uninsertable or generated columns.
Sources: drizzle-arktype/src/schema.types.ts:28-48
Column to ArkType Conversion Pipeline
Column Data Type Routing
The columnToSchema function inspects incoming Drizzle column definitions and routes them to specific ArkType validators. If a column has associated enumeration values, it generates an enumerated validator using column.enumValues, or falls back to type.string if the enumeration is empty.
export function columnToSchema(column: Column): Type {
let schema!: Type;
if (isWithEnum(column)) {
schema = column.enumValues.length ? type.enumerated(...column.enumValues) : type.string;
}
Sources: drizzle-arktype/src/column.ts:66-72
For specialized geometric, vector, and line types, columns map directly to fixed-tuple structures or object shapes with coordinate properties. Array columns recursively resolve their base column schema via columnToSchema and enforce exact element dimensions when a column size or dimension is specified.
| Column Type Matcher | Column Subtypes Checked | Generated ArkType Structure |
|---|---|---|
| Tuple Geometry / Point | PgGeometry, PgPointTuple | type([type.number, type.number]) |
| Object Geometry / Point | PgGeometryObject, PgPointObject | type({ x: type.number, y: type.number }) |
| Vector | PgHalfVector, PgVector | type.number.array() (with optional .exactlyLength(column.dimensions)) |
| Line Tuple | PgLine | type([type.number, type.number, type.number]) |
| Line ABC Object | PgLineABC | type({ a: type.number, b: type.number, c: type.number }) |
| Array | PgArray | columnToSchema(column.baseColumn).array() (with optional .exactlyLength(column.size)) |
// Handle specific types
if (isColumnType<PgGeometry<any> | PgPointTuple<any>>(column, ['PgGeometry', 'PgPointTuple'])) {
schema = type([type.number, type.number]);
} else if (
isColumnType<PgPointObject<any> | PgGeometryObject<any>>(column, ['PgGeometryObject', 'PgPointObject'])
) {
schema = type({
x: type.number,
y: type.number,
});
} else if (isColumnType<PgHalfVector<any> | PgVector<any>>(column, ['PgHalfVector', 'PgVector'])) {
schema = column.dimensions
? type.number.array().exactlyLength(column.dimensions)
: type.number.array();
} else if (isColumnType<PgLineTuple<any>>(column, ['PgLine'])) {
schema = type([type.number, type.number, type.number]);
} else if (isColumnType<PgLineABC<any>>(column, ['PgLineABC'])) {
schema = type({
a: type.number,
b: type.number,
c: type.number,
});
} // Handle other types
else if (isColumnType<PgArray<any, any>>(column, ['PgArray'])) {
const arraySchema = columnToSchema(column.baseColumn).array();
schema = column.size ? arraySchema.exactlyLength(column.size) : arraySchema;
} else if (column.dataType === 'array') {
schema = type.unknown.array();Sources: drizzle-arktype/src/column.ts:74-101
Primitive and Complex Type Mappings
Standard data types are resolved by checking column.dataType. Numeric and bigint columns delegate to dedicated boundary-resolution functions, while booleans map to type.boolean, dates map to type.Date, JSON columns map to jsonSchema, buffers map to bufferSchema, and custom types fall back to type.unknown.
} else if (column.dataType === 'number') {
schema = numberColumnToSchema(column);
} else if (column.dataType === 'bigint') {
schema = bigintColumnToSchema(column);
} else if (column.dataType === 'boolean') {
schema = type.boolean;
} else if (column.dataType === 'date') {
schema = type.Date;
} else if (column.dataType === 'string') {
schema = stringColumnToSchema(column);
} else if (column.dataType === 'json') {
schema = jsonSchema;
} else if (column.dataType === 'custom') {
schema = type.unknown;
} else if (column.dataType === 'buffer') {
schema = bufferSchema;
}Sources: drizzle-arktype/src/column.ts:102-118
Note
If a column matches none of the recognized database types or enumerations, columnToSchema defaults to type.unknown as a safe fallback validator.
Sources: drizzle-arktype/src/column.ts:121-125
Column Nullability and Operational Handling
The HandleColumn type utility determines whether a generated schema requires nullability or optionality wrappers based on whether an operation targets select, insert, or update statements. Nullable columns wrap their underlying schemas with ArktypeNullable, allowing null values, whereas defaultable insert columns and all update columns incorporate ArktypeOptional.
export type HandleColumn<
TType extends 'select' | 'insert' | 'update',
TColumn extends Column,
> = TType extends 'select' ? HandleSelectColumn<GetArktypeType<TColumn>, TColumn>
: TType extends 'insert' ? HandleInsertColumn<GetArktypeType<TColumn>, TColumn>
: TType extends 'update' ? HandleUpdateColumn<GetArktypeType<TColumn>, TColumn>
: GetArktypeType<TColumn>;Sources: drizzle-arktype/src/column.types.ts:35-41
Important
JSON columns use specialized type extraction via GetArktypeType, which resolves unconstrained JSON data columns to the Json union type rather than standard unknown descriptors.
Sources: drizzle-arktype/src/column.types.ts:9-14
Table and View Column Resolution
Column Extraction and Recursive Traversal
The getColumns helper inspects whether an incoming entity is a table or view, invoking getTableColumns or getViewSelectedFields respectively to extract its base field mappings.
function getColumns(tableLike: Table | View) {
return isTable(tableLike) ? getTableColumns(tableLike) : getViewSelectedFields(tableLike);
}Sources: drizzle-arktype/src/schema.ts:10-12
The handleColumns engine iterates over column records and recursively processes nested object relations or inline selections whenever an entry is an object and not an instance of Column, SQL, or SQL.Aliased.
function handleColumns(
columns: Record<string, any>,
refinements: Record<string, any>,
conditions: Conditions,
): Type {
const columnSchemas: Record<string, Type> = {};
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);
continue;
}Sources: drizzle-arktype/src/schema.ts:14-26
Conditional Constraints and Refinement Processing
During column iteration, handleColumns applies operational conditions and field-level user refinements to build the final composite Type schema object.
const refinement = refinements[key];
if (
refinement !== undefined
&& (typeof refinement !== 'function' || (typeof refinement === 'function' && refinement.expression !== undefined))
) {
columnSchemas[key] = refinement;
continue;
}
const column = is(selected, Column) ? selected : undefined;
const schema = column ? columnToSchema(column) : type.unknown;
const refined = typeof refinement === 'function' ? refinement(schema) : schema;
if (conditions.never(column)) {
continue;
} else {
columnSchemas[key] = refined;
}
if (column) {
if (conditions.nullable(column)) {
columnSchemas[key] = columnSchemas[key]!.or(type.null);
}
if (conditions.optional(column)) {
columnSchemas[key] = columnSchemas[key]!.optional() as any;
}
}
}
return type(columnSchemas);
}Sources: drizzle-arktype/src/schema.ts:27-59
Note
When conditions.never(column) evaluates to true—such as for database-generated columns in insert or update schemas—the property is omitted from the resulting ArkType definition entirely.
Sources: drizzle-arktype/src/schema.ts:41-45
Important
The createSelectSchema, createInsertSchema, and createUpdateSchema factories supply distinct condition rulesets governing nullability (!column.notNull), optionality (!column.notNull || (column.notNull && column.hasDefault) for inserts, always true for updates), and exclusion (generated.type === 'always').
Sources: drizzle-arktype/src/schema.ts:61-98
Static Type Inference and Refinements
Compile-Time Schema Inference and Utilities
Internal type utilities orchestrate compile-time schema composition, parameter validation, and refinement mapping across select, insert, and update operations. The BuildSchema type maps table columns or view fields into an instantiated ArkType object schema, omitting any columns marked as generated always via ColumnIsGeneratedAlwaysAs.
export type BuildSchema<
TType extends 'select' | 'insert' | 'update',
TColumns extends Record<string, any>,
TRefinements extends Record<string, any> | undefined,
> = type.instantiate<
Simplify<
{
readonly [K in keyof TColumns as ColumnIsGeneratedAlwaysAs<TColumns[K]> extends true ? never : K]:
TColumns[K] extends infer TColumn extends Column
? IsRefinementDefined<TRefinements, K> extends true
? HandleRefinement<TType, TRefinements[K & keyof TRefinements], TColumn>
: HandleColumn<TType, TColumn>
: TColumns[K] extends infer TNested extends SelectedFieldsFlat<Column> | Table | View ? BuildSchema<
TType,
GetSelection<TNested>,
TRefinements extends object ? TRefinements[K & keyof TRefinements] : undefined
>
: any;
}
>
>;Sources: drizzle-arktype/src/schema.types.internal.ts:43-63
The Conditions interface specifies functional predicates that determine whether columns are excluded, optional, or nullable during schema generation.
export interface Conditions {
never: (column?: Column) => boolean;
optional: (column: Column) => boolean;
nullable: (column: Column) => boolean;
}Sources: drizzle-arktype/src/schema.types.internal.ts:6-10
| Condition Utility | Parameter Type | Return Type | Purpose |
|---|---|---|---|
never | column?: Column | boolean | Determines if a column should be omitted entirely from the generated schema |
optional | column: Column | boolean | Evaluates whether a column accepts optional input values |
nullable | column: Column | boolean | Evaluates whether a column permits null values |
Sources: drizzle-arktype/src/schema.types.internal.ts:6-10
Field Refinement and Validation Typings
User-supplied field refinements are typed and validated through dedicated utility types that ensure type safety and prevent unknown keys. BuildRefine constructs a mapped type over table columns, accepting either a generic ArkType schema, a refinement function returning a generic schema, or nested selections.
export type BuildRefine<
TColumns extends Record<string, any>,
> = {
[K in keyof TColumns as TColumns[K] extends Column | SelectedFieldsFlat<Column> | Table | View ? K : never]?:
TColumns[K] extends Column ? BuildRefineField<GetArktypeType<TColumns[K]>>
: BuildRefine<GetSelection<TColumns[K]>>;
};Sources: drizzle-arktype/src/schema.types.internal.ts:16-22
The NoUnknownKeys utility validates refinement keys against comparison schemas, returning a DrizzleTypeError when an unrecognized key is encountered.
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, GenericSchema> ? NoUnknownKeys<TRefinement[K], TCompare[K]>
: TRefinement[K]
: DrizzleTypeError<`Found unknown key in refinement: "${K & string}"`>;
};Sources: drizzle-arktype/src/schema.types.internal.ts:65-73
Caution
Supplying an unrecognized key within a refinement object triggers NoUnknownKeys to evaluate a DrizzleTypeError string literal type, failing compilation.
Sources: drizzle-arktype/src/schema.types.internal.ts:65-73
Refinement Resolution and Operation Handling
The HandleRefinement type processes user-defined refinement functions or static schemas based on the operation context (select, insert, or update). If a refinement is defined as a function, HandleRefinement checks column nullability and applies ArktypeNullable when not strictly not-null, and wraps the resulting schema in ArktypeOptional for update operations.
type HandleRefinement<
TType extends 'select' | 'insert' | 'update',
TRefinement,
TColumn extends Column,
> = TRefinement extends (schema: any) => GenericSchema ? (
TColumn['_']['notNull'] extends true ? ReturnType<TRefinement>
: ArktypeNullable<ReturnType<TRefinement>>
) extends infer TSchema ? TType extends 'update' ? ArktypeOptional<TSchema>
: TSchema
: Type<any>
: TRefinement;Sources: drizzle-arktype/src/schema.types.internal.ts:24-34
The presence of a refinement for any given key is verified by IsRefinementDefined, which inspects whether the refinement property extends GenericSchema or a valid refinement builder function.
type IsRefinementDefined<
TRefinements extends Record<string | symbol | number, any> | undefined,
TKey extends string | symbol | number,
> = TRefinements extends object ? TRefinements[TKey] extends GenericSchema | ((schema: any) => any) ? true
: false
: false;Sources: drizzle-arktype/src/schema.types.internal.ts:36-41
Note
During schema building, IsRefinementDefined acts as a conditional gate: when it evaluates to true, HandleRefinement processes the custom rule; otherwise, standard column rules via HandleColumn take effect.
Sources: drizzle-arktype/src/schema.types.internal.ts:51-54
Specialized Column Schemas and Helpers
Standalone Column Builders and Primitives
The library exports standalone schema definitions and helper utilities designed for validating specific runtime structures, primitive types, and buffer values without requiring a full table configuration. The literalSchema accepts strings, numbers, booleans, and null values, serving as the foundation for broader structural definitions.
export const literalSchema = type.string.or(type.number).or(type.boolean).or(type.null);
export const jsonSchema = literalSchema.or(type.unknown.as<any>().array()).or(type.object.as<Record<string, any>>());
export const bufferSchema = type.unknown.narrow((value) => value instanceof Buffer).as<Buffer>().describe( // eslint-disable-line no-instanceof/no-instanceof
'a Buffer instance',
);Sources: drizzle-arktype/src/column.ts:60-64
The jsonSchema composes literalSchema with unknown arrays and arbitrary object records to validate nested JSON payloads. Meanwhile, bufferSchema uses a narrowing check to enforce runtime validation for Buffer instances with a descriptive error string.
export const jsonSchema = literalSchema.or(type.unknown.as<any>().array()).or(type.object.as<Record<string, any>>());
export const bufferSchema = type.unknown.narrow((value) => value instanceof Buffer).as<Buffer>().describe( // eslint-disable-line no-instanceof/no-instanceof
'a Buffer instance',
);Sources: drizzle-arktype/src/column.ts:61-64
| Helper Schema | Underlying Definition | Target Runtime Structure |
|---|---|---|
literalSchema | type.string.or(type.number).or(type.boolean).or(type.null) | Primitive scalars and null values |
jsonSchema | literalSchema.or(type.unknown.array()).or(type.object) | Nested JSON-compatible data structures |
bufferSchema | type.unknown.narrow((value) => value instanceof Buffer) | Node.js Buffer byte arrays |
Sources: drizzle-arktype/src/column.ts:60-64
Enum Constraint Checking and Resolution
Column definitions containing restricted sets of allowed values are processed using isWithEnum to determine whether an explicit enumeration constraint exists. When enum values are present and non-empty, columnToSchema constructs an ArkType enumerated type using type.enumerated(...column.enumValues). If the column lacks explicit values or the array is empty, it falls back to a standard string type.
if (isWithEnum(column)) {
schema = column.enumValues.length ? type.enumerated(...column.enumValues) : type.string;
}Sources: drizzle-arktype/src/column.ts:69-71
The utility function isWithEnum verifies column configurations by checking whether the enumValues property exists on the column object, is an array, and contains a positive length. The related isPgEnum helper casts this check to validate PostgreSQL-specific enum entities.
export function isWithEnum(column: Column): column is typeof column & { enumValues: [string, ...string[]] } {
return 'enumValues' in column && Array.isArray(column.enumValues) && column.enumValues.length > 0;
}
export const isPgEnum: (entity: any) => entity is PgEnum<[string, ...string[]]> = isWithEnum as any;Sources: drizzle-arktype/src/utils.ts:10-14
Caution
Passing a column object with an empty enumValues array will bypass the enumerated schema builder and default to an unrestricted string validator.
Sources: drizzle-arktype/src/column.ts:69-71
Related
On This Page
OverviewPublic Schema Generation APISchema Factory FunctionsOverloads and Refinement SignaturesColumn to ArkType Conversion PipelineColumn Data Type RoutingPrimitive and Complex Type MappingsColumn Nullability and Operational HandlingTable and View Column ResolutionColumn Extraction and Recursive TraversalConditional Constraints and Refinement ProcessingStatic Type Inference and RefinementsCompile-Time Schema Inference and UtilitiesField Refinement and Validation TypingsRefinement Resolution and Operation HandlingSpecialized Column Schemas and HelpersStandalone Column Builders and PrimitivesEnum Constraint Checking and ResolutionRelated