API Layer
zod

Zod

TypeScriptOpen SourceFree tier

TypeScript-first schema validation with static type inference. Define a schema once and get both runtime validation and TypeScript types. The standard for validating API inputs, forms, and environment variables.

License

MIT

Language

TypeScript

Used for
93
Trust
Excellent

Why Zod?

Validating API request bodies, form inputs, or env vars

You want TypeScript type inference from your validation schema

Using with tRPC, React Hook Form, or Next.js server actions

Signal Breakdown

What drives the Trust Score

Weekly npm downloads
26M/wk
GitHub commits (90d)
95
GitHub stars
34k
Stack Overflow questions
25k
Community health
Excellent
Weighted Trust Score93 / 100

Download Trend

Last 12 months

Tradeoffs & Caveats

Know before you commit

Python project — use Pydantic instead

Simple one-off validation — native TypeScript types may suffice

Schema size is a concern — Zod bundle is ~13kb gzipped

Pricing

Free tier & paid plans

Free tier

Open source, free forever

Paid

N/A

MIT license

Often Used Together

Complementary tools that pair well with Zod

trpc

tRPC

API Layer

87Strong
View
nextjs

Next.js

Frontend & UI

98Excellent
View
prisma

Prisma

Database & Cache

88Strong
View
fastapi

FastAPI

Backend Frameworks

97Excellent
View
vercel

Vercel

Hosting & Deploy

89Strong
View

Learning Resources

Docs, videos, tutorials, and courses

Get Started

Repository and installation options

View on GitHub

github.com/colinhacks/zod

npmnpm install zod

Quick Start

Copy and adapt to get going fast

import { z } from 'zod';

// Define schema
const EnvSchema = z.object({
  DATABASE_URL: z.string().url(),
  PORT: z.coerce.number().default(3000),
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});

// Validate environment at startup
export const env = EnvSchema.parse(process.env);
// env.PORT is typed as number, env.DATABASE_URL as string

Code Examples

Common usage patterns

API route validation with Next.js

Validate request body in a server action

// app/actions.ts
'use server';
import { z } from 'zod';

const CreatePostSchema = z.object({
  title: z.string().min(3).max(200),
  content: z.string().min(10),
  tags: z.array(z.string()).max(5).default([]),
});

export async function createPost(formData: FormData) {
  const raw = Object.fromEntries(formData);
  const data = CreatePostSchema.parse(raw);
  return db.posts.create({ data });
}

Discriminated union

Type-safe result types with Zod

import { z } from 'zod';

const ApiResponse = z.discriminatedUnion('status', [
  z.object({ status: z.literal('success'), data: z.array(z.string()) }),
  z.object({ status: z.literal('error'), code: z.number(), message: z.string() }),
]);

const response = await fetch('/api/items').then(r => r.json());
const parsed = ApiResponse.parse(response);

if (parsed.status === 'success') {
  console.log(parsed.data); // string[]
} else {
  console.error(parsed.message); // string
}

Community Notes

Real experiences from developers who've used this tool