DEV Community

Alex Spinov
Alex Spinov

Posted on

Zod Has a Free Validation Library — Type-Safe Data Validation in TypeScript

The Validation Problem

Your API receives a request body. TypeScript tells you it is type-safe. But TypeScript types disappear at runtime. That user: User could be anything — a string, null, or {hack: true}.

You need runtime validation. And it should match your TypeScript types.

Zod: Schema Validation That Generates Types

Zod is a TypeScript-first schema validation library. Define the schema once, get both runtime validation AND TypeScript types.

Basic Usage

import { z } from 'zod'

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive().optional()
})

// TypeScript type is GENERATED from the schema
type User = z.infer<typeof UserSchema>
// { name: string; email: string; age?: number }

// Runtime validation
const result = UserSchema.safeParse(requestBody)
if (!result.success) {
  console.log(result.error.issues)
  // [{ code: 'invalid_string', message: 'Invalid email', path: ['email'] }]
} else {
  const user = result.data // Fully typed as User
}
Enter fullscreen mode Exit fullscreen mode

One source of truth. No type/validation drift.

Why 30M+ Weekly Downloads

1. API Input Validation

app.post('/users', (req, res) => {
  const result = UserSchema.safeParse(req.body)
  if (!result.success) {
    return res.status(400).json({ errors: result.error.flatten() })
  }
  // result.data is guaranteed to match User type
  createUser(result.data)
})
Enter fullscreen mode Exit fullscreen mode

2. Environment Variable Validation

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

export const env = EnvSchema.parse(process.env)
// Crashes at startup if env vars are wrong — not at 3 AM in production
Enter fullscreen mode Exit fullscreen mode

3. Form Validation (React)

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'

const form = useForm({
  resolver: zodResolver(UserSchema)
})
// Form validation matches API validation — same schema
Enter fullscreen mode Exit fullscreen mode

4. API Response Validation

const ApiResponse = z.object({
  data: z.array(UserSchema),
  total: z.number()
})

const response = await fetch('/api/users')
const validated = ApiResponse.parse(await response.json())
// If the API changes shape, you know immediately — not in a bug report
Enter fullscreen mode Exit fullscreen mode

Zod vs Alternatives

Feature Zod Joi Yup
TypeScript-first Yes No Partial
Type inference Full Manual Partial
Bundle size 13KB 150KB 40KB
Ecosystem Huge Legacy Moderate
Error messages Structured Strings Strings

The Zod Ecosystem

  • tRPC: Uses Zod for end-to-end type-safe APIs
  • React Hook Form: Official Zod resolver
  • Next.js: Server Actions use Zod for validation
  • Astro: Content collections use Zod schemas
  • Drizzle: Generates Zod schemas from database tables

Install

npm install zod
Enter fullscreen mode Exit fullscreen mode

Zero dependencies. 13KB gzipped. Works everywhere JavaScript runs.


Building data pipelines? I maintain 88+ web scrapers on Apify. Extract data from Reddit, Trustpilot, HN, Google News — structured JSON/CSV ready for your app. Custom scrapers: spinov001@gmail.com

Top comments (0)