WARNING

Starting from drizzle-orm@1.0.0-beta.15, drizzle-zod has been deprecated in favor of first-class schema generation support within Drizzle ORM itself

You can still use drizzle-zod package but all new update will be added to Drizzle ORM directly

zod

Install the dependencies

npm
yarn
pnpm
bun
npm i zod

Select schema

Defines the shape of data queried from the database - can be used to validate API responses.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createSelectSchema } from 'drizzle-orm/zod';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userSelectSchema = createSelectSchema(users);

const rows = await db.select({ id: users.id, name: users.name }).from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Error: `age` is not returned in the above query

const rows = await db.select().from(users).limit(1);
const parsed: { id: number; name: string; age: number } = userSelectSchema.parse(rows[0]); // Will parse successfully

Column enums are supported through the SingleStore table schema and can be validated with the generated table schemas.

Insert schema

Defines the shape of data to be inserted into the database - can be used to validate API requests.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createInsertSchema } from 'drizzle-orm/zod';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userInsertSchema = createInsertSchema(users);

const user = { name: 'John' };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Error: `age` is not defined

const user = { name: 'Jane', age: 30 };
const parsed: { name: string, age: number } = userInsertSchema.parse(user); // Will parse successfully
await db.insert(users).values(parsed);

Update schema

Defines the shape of data to be updated in the database - can be used to validate API requests.

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createUpdateSchema } from 'drizzle-orm/zod';

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const userUpdateSchema = createUpdateSchema(users);

const user = { id: 5, name: 'John' };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema.parse(user); // Error: `id` is a generated column, it can't be updated

const user = { age: 35 };
const parsed: { name?: string | undefined, age?: number | undefined } = userUpdateSchema.parse(user); // Will parse successfully
await db.update(users).set(parsed).where(eq(users.name, 'Jane'));

Refinements

Each create schema function accepts an additional optional parameter that you can used to extend, modify or completely overwite a field’s schema. Defining a callback function will extend or modify while providing a Zod schema will overwrite it.

import { int, json, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createSelectSchema } from 'drizzle-orm/zod';
import { z } from 'zod/v4';

const users = singlestoreTable('users', {
  id: int().primaryKey(),
  name: text().notNull(),
  bio: text(),
  preferences: json()
});

const userSelectSchema = createSelectSchema(users, {
  name: (schema) => schema.max(20), // Extends schema
  bio: (schema) => schema.max(1000), // Extends schema before becoming nullable/optional
  preferences: z.object({ theme: z.string() }) // Overwrites the field, including its nullability
});

const parsed: {
  id: number;
  name: string,
  bio?: string | undefined;
  preferences: {
    theme: string;
  };
} = userSelectSchema.parse(...);

Factory functions

For more advanced use cases, you can use the createSchemaFactory function.

Use case: Using an extended Zod instance

import { int, singlestoreTable, text } from 'drizzle-orm/singlestore-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from '@hono/zod-openapi'; // Extended Zod instance

const users = singlestoreTable('users', {
  id: int().primaryKey().autoincrement(),
  name: text().notNull(),
  age: int().notNull()
});

const { createInsertSchema } = createSchemaFactory({ zodInstance: z });

const userInsertSchema = createInsertSchema(users, {
  // We can now use the extended instance
  name: (schema) => schema.openapi({ example: 'John' })
});

Use case: Type coercion

import { singlestoreTable, timestamp } from 'drizzle-orm/singlestore-core';
import { createSchemaFactory } from 'drizzle-orm/zod';
import { z } from 'zod/v4';

const users = singlestoreTable('users', {
  ...,
  createdAt: timestamp().notNull()
});

const { createInsertSchema } = createSchemaFactory({
  // This configuration will only coerce dates. Set `coerce` to `true` to coerce all data types or specify others
  coerce: {
    date: true
  }
});

const userInsertSchema = createInsertSchema(users);
// The above is the same as this:
const userInsertSchema = z.object({
  ...,
  createdAt: z.coerce.date()
});

Data type reference

singlestore.boolean();

// Schema
z.boolean();
singlestore.date({ mode: 'date' });
singlestore.datetime({ mode: 'date' });
singlestore.timestamp({ mode: 'date' });

// Schema
z.date();
singlestore.binary();
singlestore.date({ mode: 'string' });
singlestore.datetime({ mode: 'string' });
singlestore.decimal();
singlestore.time();
singlestore.timestamp({ mode: 'string' });
singlestore.varbinary();

// Schema
z.string();
singlestore.char({ length: ... });

// Schema
z.string().length(length);
singlestore.varchar({ length: ... });

// Schema
z.string().max(length);
singlestore.text();

// Schema
z.string().max(65_535); // unsigned 16-bit integer limit
singlestore.text({ enum: ... });
singlestore.char({ enum: ... });
singlestore.varchar({ enum: ... });
singlestore.singlestoreEnum(..., ...);

// Schema
z.enum(enum);
singlestore.tinyint();

// Schema
z.number().min(-128).max(127).int(); // 8-bit integer lower and upper limit
singlestore.tinyint({ unsigned: true });

// Schema
z.number().min(0).max(255).int(); // unsigned 8-bit integer lower and upper limit
singlestore.smallint();

// Schema
z.number().min(-32_768).max(32_767).int(); // 16-bit integer lower and upper limit
singlestore.smallint({ unsigned: true });

// Schema
z.number().min(0).max(65_535).int(); // unsigned 16-bit integer lower and upper limit
singlestore.float();

// Schema
z.number().min(-8_388_608).max(8_388_607); // 24-bit integer lower and upper limit
singlestore.mediumint();

// Schema
z.number().min(-8_388_608).max(8_388_607).int(); // 24-bit integer lower and upper limit
singlestore.float({ unsigned: true });

// Schema
z.number().min(0).max(16_777_215); // unsigned 24-bit integer lower and upper limit
singlestore.mediumint({ unsigned: true });

// Schema
z.number().min(0).max(16_777_215).int(); // unsigned 24-bit integer lower and upper limit
singlestore.int();

// Schema
z.number().min(-2_147_483_648).max(2_147_483_647).int(); // 32-bit integer lower and upper limit
singlestore.int({ unsigned: true });

// Schema
z.number().min(0).max(4_294_967_295).int(); // unsgined 32-bit integer lower and upper limit
singlestore.double();
singlestore.real();

// Schema
z.number().min(-140_737_488_355_328).max(140_737_488_355_327); // 48-bit integer lower and upper limit
singlestore.double({ unsigned: true });

// Schema
z.number().min(0).max(281_474_976_710_655); // unsigned 48-bit integer lower and upper limit
singlestore.bigint({ mode: 'number' });

// Schema
z.number().min(-9_007_199_254_740_991).max(9_007_199_254_740_991).int(); // Javascript min. and max. safe integers
singlestore.serial();

// Schema
z.number().min(0).max(9_007_199_254_740_991).int(); // Javascript max. safe integer
singlestore.bigint({ mode: 'bigint' });

// Schema
z.bigint().min(-9_223_372_036_854_775_808n).max(9_223_372_036_854_775_807n); // 64-bit integer lower and upper limit
singlestore.bigint({ mode: 'bigint', unsigned: true });

// Schema
z.bigint().min(0).max(18_446_744_073_709_551_615n); // unsigned 64-bit integer lower and upper limit
singlestore.year();

// Schema
z.number().min(1_901).max(2_155).int();
singlestore.json();

// Schema
z.union([z.union([z.string(), z.number(), z.boolean(), z.null()]), z.record(z.any()), z.array(z.any())]);