DEV Community

Alex Spinov
Alex Spinov

Posted on

Zod Has a Free API — Here's How to Validate Everything in TypeScript

Zod is a TypeScript-first schema validation library. It lets you define schemas once and get both runtime validation and TypeScript types — eliminating the duplication between types and validation.

Installation

npm install zod
Enter fullscreen mode Exit fullscreen mode

Basic Schemas

import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().positive().max(150),
  role: z.enum(["admin", "user", "moderator"]),
  bio: z.string().optional()
});

// Infer TypeScript type
type User = z.infer<typeof userSchema>;

// Validate
const result = userSchema.safeParse({
  name: "Alex",
  email: "alex@dev.com",
  age: 28,
  role: "admin"
});

if (result.success) {
  console.log(result.data.name); // Fully typed!
} else {
  console.log(result.error.issues); // Detailed error messages
}
Enter fullscreen mode Exit fullscreen mode

Advanced Types

// Arrays and tuples
const tags = z.array(z.string()).min(1).max(10);
const pair = z.tuple([z.string(), z.number()]);

// Unions and discriminated unions
const result = z.discriminatedUnion("status", [
  z.object({ status: z.literal("success"), data: z.any() }),
  z.object({ status: z.literal("error"), message: z.string() })
]);

// Records
const config = z.record(z.string(), z.unknown());

// Recursive types
const categorySchema: z.ZodType<Category> = z.lazy(() =>
  z.object({
    name: z.string(),
    children: z.array(categorySchema)
  })
);
Enter fullscreen mode Exit fullscreen mode

Transforms

const stringToNumber = z.string().transform(Number);
const result = stringToNumber.parse("42"); // 42 (number)

const dateString = z.string().transform(s => new Date(s));

// Coercion
const coerced = z.coerce.number(); // "42" -> 42
const coercedDate = z.coerce.date(); // "2026-01-01" -> Date
Enter fullscreen mode Exit fullscreen mode

API Validation

const createPostSchema = z.object({
  body: z.object({
    title: z.string().min(1).max(200),
    content: z.string().min(10),
    tags: z.array(z.string()).default([])
  }),
  params: z.object({}),
  query: z.object({
    draft: z.coerce.boolean().default(false)
  })
});

// In Express/Fastify handler
const validated = createPostSchema.parse({
  body: req.body,
  params: req.params,
  query: req.query
});
Enter fullscreen mode Exit fullscreen mode

Error Formatting

const result = userSchema.safeParse(badData);
if (!result.success) {
  const formatted = result.error.format();
  console.log(formatted.email?._errors); // ["Invalid email"]
  console.log(result.error.flatten()); // { fieldErrors: { email: ["Invalid email"] } }
}
Enter fullscreen mode Exit fullscreen mode

Need to extract or automate web content at scale? Check out my web scraping tools on Apify — no coding required. Or email me at spinov001@gmail.com for custom solutions.

Top comments (0)