Skip to main content
Drizzle provides official plugins to generate runtime validation schemas from your Drizzle ORM schemas. These plugins automatically create validators for insert, update, and select operations, ensuring type safety at both compile time and runtime.

Available Validators

Drizzle supports four popular validation libraries:

Zod

The most popular TypeScript-first schema validation library

Valibot

Modular and lightweight validation library

TypeBox

JSON Schema Type Builder with static type inference

ArkType

TypeScript-native runtime validation with 1:1 type/validator syntax

Zod

Installation

npm install drizzle-zod zod

Basic Usage

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-zod';
import { z } from 'zod';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  role: text('role', { enum: ['admin', 'user'] }).notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

// Generate schemas
const insertUserSchema = createInsertSchema(users);
const selectUserSchema = createSelectSchema(users);
const updateUserSchema = createUpdateSchema(users);

// Use in your application
const newUser = insertUserSchema.parse({
  name: 'John Doe',
  email: '[email protected]',
  role: 'user',
});

Customizing Schemas

Override Field Validation

import { createInsertSchema } from 'drizzle-zod';
import { z } from 'zod';

const insertUserSchema = createInsertSchema(users, {
  email: z.string().email(),
  name: z.string().min(3).max(255),
  role: z.enum(['admin', 'user', 'moderator']),
});

Refine Fields

Refine fields before they become optional/nullable:
const insertUserSchema = createInsertSchema(users, {
  email: (schema) => schema.email(),
  id: (schema) => schema.positive(),
});

Pick Specific Fields

const createUserRequestSchema = insertUserSchema.pick({
  name: true,
  email: true,
  role: true,
});

const updateUserRequestSchema = updateUserSchema.pick({
  name: true,
  email: true,
}).partial();

API Validation Example

import { createInsertSchema } from 'drizzle-zod';
import { drizzle } from 'drizzle-orm/node-postgres';

const insertUserSchema = createInsertSchema(users).omit({ id: true });

app.post('/users', async (req, res) => {
  // Validate request body
  const result = insertUserSchema.safeParse(req.body);
  
  if (!result.success) {
    return res.status(400).json({ errors: result.error.errors });
  }
  
  // Insert validated data
  const [user] = await db.insert(users).values(result.data).returning();
  
  res.json(user);
});

Features

  • Create select schemas for tables, views, and enums
  • Create insert and update schemas for tables
  • Full type inference from Drizzle schemas
  • Supports all PostgreSQL, MySQL, and SQLite dialects
  • Refine and customize generated schemas

Valibot

Installation

npm install drizzle-valibot valibot

Basic Usage

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-valibot';
import { parse, email, minLength, pipe, string } from 'valibot';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  role: text('role', { enum: ['admin', 'user'] }).notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

// Generate schemas
const insertUserSchema = createInsertSchema(users);
const selectUserSchema = createSelectSchema(users);
const updateUserSchema = createUpdateSchema(users);

// Validate data
const validUser = parse(insertUserSchema, {
  name: 'John Doe',
  email: '[email protected]',
  role: 'user',
});

Customizing Schemas

import { createInsertSchema } from 'drizzle-valibot';
import { email, minLength, pipe, string } from 'valibot';

const insertUserSchema = createInsertSchema(users, {
  email: pipe(string(), email()),
  name: pipe(string(), minLength(3)),
  role: string(),
});

Refining Fields

const insertUserSchema = createInsertSchema(users, {
  id: (schema) => pipe(schema, minValue(1)),
  email: (schema) => pipe(schema, email()),
});

TypeBox

Installation

npm install drizzle-typebox @sinclair/typebox

Basic Usage

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-typebox';
import { Type } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  role: text('role', { enum: ['admin', 'user'] }).notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

// Generate schemas
const insertUserSchema = createInsertSchema(users);
const selectUserSchema = createSelectSchema(users);
const updateUserSchema = createUpdateSchema(users);

// Validate data
const isValid = Value.Check(insertUserSchema, {
  name: 'John Doe',
  email: '[email protected]',
  role: 'user',
});

Customizing Schemas

import { createInsertSchema } from 'drizzle-typebox';
import { Type } from '@sinclair/typebox';

const insertUserSchema = createInsertSchema(users, {
  email: Type.String({ format: 'email' }),
  name: Type.String({ minLength: 3, maxLength: 255 }),
  role: Type.String(),
});

Refining Fields

const insertUserSchema = createInsertSchema(users, {
  id: (schema) => Type.Number({ ...schema, minimum: 1 }),
  email: Type.String({ format: 'email' }),
});

ArkType

Installation

npm install drizzle-arktype arktype

Basic Usage

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-arktype';
import { type } from 'arktype';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  role: text('role', { enum: ['admin', 'user'] }).notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

// Generate schemas
const insertUserSchema = createInsertSchema(users);
const selectUserSchema = createSelectSchema(users);
const updateUserSchema = createUpdateSchema(users);

// Validate data
const result = insertUserSchema({
  name: 'John Doe',
  email: '[email protected]',
  role: 'user',
});

Customizing Schemas

import { createInsertSchema } from 'drizzle-arktype';
import { type } from 'arktype';

const insertUserSchema = createInsertSchema(users, {
  email: type('string.email'),
  name: type('string'),
  role: type('"admin" | "user"'),
});

Refining Fields

const insertUserSchema = createInsertSchema(users, {
  id: (schema) => schema.atLeast(1),
  role: type('string'),
});

Comparison

LibraryMinifiedGzipped
ValibotSmallest~5 KB
ArkTypeSmall~12 KB
TypeBoxMedium~15 KB
ZodLarger~14 KB

Common Patterns

Multi-step Form Validation

import { createInsertSchema } from 'drizzle-zod';
import { z } from 'zod';

const userSchema = createInsertSchema(users);

// Step 1: Basic info
const step1Schema = userSchema.pick({
  name: true,
  email: true,
});

// Step 2: Additional info
const step2Schema = userSchema.pick({
  role: true,
  bio: true,
});

// Combine for final validation
const completeUserSchema = step1Schema.merge(step2Schema);

API Request/Response Validation

import { createInsertSchema, createSelectSchema } from 'drizzle-zod';

// Request validation (insert)
const createUserRequest = createInsertSchema(users).omit({
  id: true,
  createdAt: true,
});

// Response validation (select)
const userResponse = createSelectSchema(users).omit({
  password: true, // Never expose passwords
});

app.post('/users', async (req, res) => {
  const data = createUserRequest.parse(req.body);
  const [user] = await db.insert(users).values(data).returning();
  const response = userResponse.parse(user);
  res.json(response);
});

Partial Updates

import { createUpdateSchema } from 'drizzle-zod';

const updateUserSchema = createUpdateSchema(users)
  .omit({ id: true, createdAt: true })
  .partial(); // All fields optional

app.patch('/users/:id', async (req, res) => {
  const data = updateUserSchema.parse(req.body);
  const [user] = await db
    .update(users)
    .set(data)
    .where(eq(users.id, req.params.id))
    .returning();
  res.json(user);
});

Nested Relations Validation

import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
import { z } from 'zod';

const insertPostSchema = createInsertSchema(posts);
const insertCommentSchema = createInsertSchema(comments);

// Validate post with nested comments
const createPostWithComments = insertPostSchema.extend({
  comments: z.array(insertCommentSchema.omit({ postId: true })).optional(),
});

Framework Integration

Next.js Server Actions

app/actions.ts
'use server';

import { createInsertSchema } from 'drizzle-zod';
import { db } from '@/db';

const insertUserSchema = createInsertSchema(users).omit({ id: true });

export async function createUser(formData: FormData) {
  const data = insertUserSchema.parse(Object.fromEntries(formData));
  return await db.insert(users).values(data).returning();
}

tRPC Integration

server/routers/users.ts
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
import { router, publicProcedure } from '../trpc';

const insertUserSchema = createInsertSchema(users).omit({ id: true });
const selectUserSchema = createSelectSchema(users);

export const usersRouter = router({
  create: publicProcedure
    .input(insertUserSchema)
    .output(selectUserSchema)
    .mutation(async ({ input }) => {
      const [user] = await db.insert(users).values(input).returning();
      return user;
    }),
});

Hono Validation

import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { createInsertSchema } from 'drizzle-zod';

const app = new Hono();
const insertUserSchema = createInsertSchema(users);

app.post('/users', zValidator('json', insertUserSchema), async (c) => {
  const data = c.req.valid('json');
  const [user] = await db.insert(users).values(data).returning();
  return c.json(user);
});

Best Practices

1

Generate once, reuse everywhere

Create validation schemas once and export them for use across your application:
schemas/users.ts
export const insertUserSchema = createInsertSchema(users);
export const selectUserSchema = createSelectSchema(users);
export const updateUserSchema = createUpdateSchema(users);
2

Customize for specific use cases

Create specialized schemas for different operations:
export const createUserApiSchema = insertUserSchema.omit({ id: true });
export const publicUserSchema = selectUserSchema.omit({ password: true });
3

Use safe parsing in production

Use safeParse to handle validation errors gracefully:
const result = insertUserSchema.safeParse(data);
if (!result.success) {
  return { error: result.error.flatten() };
}
4

Type inference

Infer TypeScript types from your validation schemas:
import { z } from 'zod';
type InsertUser = z.infer<typeof insertUserSchema>;

Next Steps

Drizzle Seed

Generate test data that validates against your schemas

API Development

Build type-safe APIs with Drizzle and validation

Build docs developers (and LLMs) love