DevToolBox무료
블로그

JSON to Zod Schema: TypeScript 타입 안전 런타임 유효성 검사

12분 읽기by DevToolBox

TL;DR

Zod lets you define runtime validation schemas that automatically produce TypeScript types. Converting JSON to a Zod schema gives you type-safe parsing, helpful error messages, and zero duplication between types and validation. Use our free JSON to Zod converter to generate a Zod schema from any JSON object in seconds.

Key Takeaways

  • Zod validates data at runtime and infers TypeScript types from the same schema definition
  • Every JSON structure (objects, arrays, primitives, nulls) maps to a corresponding Zod method
  • Use z.infer<typeof schema> to derive a static type from any Zod schema
  • Optional, nullable, and default values each have dedicated Zod helpers
  • Discriminated unions let you validate tagged/polymorphic JSON cleanly
  • Automated JSON-to-Zod generation eliminates boilerplate for large payloads

What Is Zod and Why Use It

Zod is a TypeScript-first schema validation library with zero dependencies. It lets you declare the shape of your data once and then get two things for free: runtime validation (parsing incoming JSON, form data, or environment variables) and static TypeScript types (via z.infer). That single-source-of-truth approach means your types and your validation logic can never drift apart.

Traditional TypeScript interfaces are erased at compile time. If an API returns an unexpected field or a missing value, TypeScript alone will not catch it. Zod fills that gap by checking every value at runtime and throwing structured errors when something is wrong.

Common use cases include validating REST or GraphQL API responses, sanitizing form input in React or Next.js, parsing environment variables at startup, and enforcing contracts between microservices. Because Zod schemas compose naturally, you can start small and scale to complex nested structures without rewriting anything.

JSON to Zod: The Conversion Concept

Converting JSON to a Zod schema means analyzing a concrete JSON value and producing a Zod schema definition that would accept that value (and structurally similar values). The process inspects every key, detects the type of each value, handles nesting, and outputs valid TypeScript code using Zod methods.

For example, given this JSON:

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "active": true
}

A JSON-to-Zod converter produces:

import { z } from "zod";

const schema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string(),
  active: z.boolean(),
});

type User = z.infer<typeof schema>;
// { id: number; name: string; email: string; active: boolean }

You can do this by hand for small payloads or use our online JSON to Zod schema generator for larger structures. If you also need a plain TypeScript interface, try the JSON to TypeScript converter.

Basic Zod Types

Zod provides a method for every JavaScript primitive. Here is how each JSON primitive maps to Zod:

JSON ValueZod SchemaTypeScript Type
"hello"z.string()string
42z.number()number
truez.boolean()boolean
nullz.null()null
undefinedz.undefined()undefined

Zod also offers refinement methods you can chain onto any primitive:

// String with constraints
const email = z.string().email();
const slug = z.string().min(1).max(100).regex(/^[a-z0-9-]+$/);

// Number with constraints
const port = z.number().int().min(1).max(65535);
const score = z.number().nonnegative();

// Literal values (exact match)
const status = z.literal("active");
const version = z.literal(2);

Objects and Nested Schemas

Most JSON payloads are objects, and z.object() is how you describe them. Nested objects simply nest z.object() calls inside each other.

Consider this JSON representing a blog post with an embedded author:

{
  "title": "Getting Started with Zod",
  "slug": "getting-started-with-zod",
  "published": true,
  "views": 1024,
  "author": {
    "name": "DevToolBox",
    "url": "https://viadreams.cc"
  },
  "tags": ["typescript", "validation", "zod"]
}

The corresponding Zod schema:

const authorSchema = z.object({
  name: z.string(),
  url: z.string().url(),
});

const postSchema = z.object({
  title: z.string().min(1),
  slug: z.string().regex(/^[a-z0-9-]+$/),
  published: z.boolean(),
  views: z.number().int().nonnegative(),
  author: authorSchema,
  tags: z.array(z.string()),
});

type Post = z.infer<typeof postSchema>;

Extracting authorSchema into its own variable is not required but keeps things readable. You can reuse it anywhere another schema needs the same author shape. The z.infer call at the bottom produces a fully typed Post interface automatically.

Zod objects also support .extend(), .merge(), .pick(), .omit(), and .partial() for composing and transforming schemas, just like TypeScript utility types. For a deeper dive into generics and utility types, see our guide on TypeScript generics.

Arrays and Tuples

JSON arrays map to either z.array() (homogeneous list) or z.tuple() (fixed-length with per-position types).

// Homogeneous array
const tags = z.array(z.string());         // string[]
const scores = z.array(z.number()).min(1); // at least one element

// Array of objects
const users = z.array(
  z.object({
    id: z.number(),
    name: z.string(),
  })
);

// Tuple: fixed length, each position typed
const coordinate = z.tuple([
  z.number(), // latitude
  z.number(), // longitude
]);
// inferred type: [number, number]

// Tuple with rest element
const row = z.tuple([z.string(), z.number()]).rest(z.boolean());
// inferred type: [string, number, ...boolean[]]

A practical example: converting a JSON array of products.

[
  { "sku": "A100", "price": 29.99, "inStock": true },
  { "sku": "B200", "price": 49.99, "inStock": false }
]
const productSchema = z.object({
  sku: z.string(),
  price: z.number().positive(),
  inStock: z.boolean(),
});

const productsSchema = z.array(productSchema);

// Parse and validate
const parsed = productsSchema.parse(jsonData);
// parsed is fully typed as { sku: string; price: number; inStock: boolean }[]

Optional, Nullable, and Default Values

JSON data often contains fields that might be missing, explicitly null, or both. Zod has distinct helpers for each scenario:

const userSchema = z.object({
  name: z.string(),

  // Field may be absent (undefined)
  nickname: z.string().optional(),
  // inferred: string | undefined

  // Field may be null
  deletedAt: z.string().nullable(),
  // inferred: string | null

  // Field may be absent OR null
  bio: z.string().nullish(),
  // inferred: string | null | undefined

  // Falls back to a default if missing
  role: z.string().default("viewer"),
  // inferred: string (always present after parsing)

  // Coerce a string to a number automatically
  age: z.coerce.number(),
  // accepts "25" and returns 25
});

These modifiers matter when converting JSON to Zod because sample JSON only shows one snapshot of the data. A field that is present in your sample might be optional in the real schema. Automated generators typically mark every field as required; you should review the output and add .optional() or .nullable() where the actual API contract allows missing values.

Here is a JSON example where some fields are null:

{
  "username": "dev_user",
  "avatar": null,
  "settings": {
    "theme": "dark",
    "language": null
  }
}
const settingsSchema = z.object({
  theme: z.string().default("light"),
  language: z.string().nullable(),
});

const profileSchema = z.object({
  username: z.string().min(1),
  avatar: z.string().url().nullable(),
  settings: settingsSchema,
});

Unions and Discriminated Unions

When a JSON field can hold different shapes, Zod provides z.union() and z.discriminatedUnion(). The discriminated variant is faster because Zod can look at a single discriminator key instead of trying every option.

// Simple union: value can be string or number
const idSchema = z.union([z.string(), z.number()]);

// Discriminated union: tagged objects
const eventSchema = z.discriminatedUnion("type", [
  z.object({
    type: z.literal("click"),
    x: z.number(),
    y: z.number(),
  }),
  z.object({
    type: z.literal("keypress"),
    key: z.string(),
    code: z.number(),
  }),
  z.object({
    type: z.literal("scroll"),
    direction: z.enum(["up", "down"]),
    delta: z.number(),
  }),
]);

type AppEvent = z.infer<typeof eventSchema>;
// TypeScript narrows the type based on "type" field

A concrete JSON payload that the schema above would validate:

{ "type": "click", "x": 120, "y": 340 }
{ "type": "keypress", "key": "Enter", "code": 13 }

Discriminated unions are especially useful for webhook payloads, event streams, and API responses that include a type or kind field. After parsing, TypeScript narrows the union automatically in switch statements and if blocks.

Using Zod with TypeScript: z.infer

The killer feature of Zod is z.infer<typeof schema>. It derives a TypeScript type from any Zod schema, eliminating the need to define types separately.

const addressSchema = z.object({
  street: z.string(),
  city: z.string(),
  zip: z.string().regex(/^\d{5}(-\d{4})?$/),
  country: z.string().length(2),
});

// Derive the type — no separate interface needed
type Address = z.infer<typeof addressSchema>;

// Use in function signatures
function formatAddress(addr: Address): string {
  return `${addr.street}, ${addr.city} ${addr.zip}, ${addr.country}`;
}

// Safe parsing: returns a result object instead of throwing
const result = addressSchema.safeParse(unknownData);
if (result.success) {
  console.log(result.data.city); // fully typed
} else {
  console.error(result.error.issues);
}

This pattern is used throughout modern TypeScript stacks. tRPC uses Zod schemas to define API input types. React Hook Form uses them for form validation. Next.js server actions parse form data with Zod before touching the database.

You can also extract input and output types separately when a schema uses .transform():

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

type DateInput = z.input<typeof dateSchema>;  // string
type DateOutput = z.output<typeof dateSchema>; // Date

For more on TypeScript type manipulation techniques, see our article on TypeScript generics explained.

Zod vs JSON Schema vs TypeScript Interfaces

Developers often ask how Zod compares to JSON Schema and plain TypeScript interfaces. Here is a side-by-side comparison:

FeatureZodJSON SchemaTS Interface
Runtime validationYes (built-in)Yes (via Ajv, etc.)No
Static typesAuto-inferredRequires codegenManual
LanguageTypeScript/JS onlyLanguage-agnosticTypeScript only
TransformsYes (parse + transform)NoNo
Error messagesRich, customizableValidator-dependentCompile-time only
Composabilityextend, merge, pick, omit$ref, allOf, oneOfextends, Omit, Pick
Best forTS apps, APIs, formsCross-language APIsInternal code types

When to use Zod: You are building a TypeScript application and need both validation and types. Zod is ideal for Next.js, Express, tRPC, and React apps.

When to use JSON Schema: Your API contract must be consumed by multiple languages (Python, Go, Java). JSON Schema is language-agnostic and has broad tooling support. Use our JSON to JSON Schema converter to generate one.

When to use plain TypeScript: For internal types that never cross a system boundary (component props, function parameters, internal state). No runtime cost, no extra dependency.

Frequently Asked Questions

How do I convert a large JSON API response to a Zod schema quickly?

Paste your JSON into our JSON to Zod converter. It analyzes every field, detects types, handles nesting, and outputs a ready-to-use Zod schema. For very large payloads, generate the base schema automatically and then refine it by adding .optional(), .email(), or custom validators where needed.

Does Zod work with JavaScript or only TypeScript?

Zod works with both. The library is written in TypeScript but ships compiled JavaScript with type declarations. In a plain JavaScript project, you still get runtime validation. However, you lose the automatic type inference that makes Zod so powerful. For JavaScript-only projects, Joi or Yup may be simpler alternatives.

How does Zod handle extra fields not defined in the schema?

By default, z.object() strips unknown keys during parsing (similar to additionalProperties: false in JSON Schema). If you want to preserve extra fields, use .passthrough(). If you want parsing to fail when extra fields appear, use .strict().

const schema = z.object({ name: z.string() });

schema.parse({ name: "Alice", extra: true });
// returns { name: "Alice" } — extra key stripped

schema.passthrough().parse({ name: "Alice", extra: true });
// returns { name: "Alice", extra: true }

schema.strict().parse({ name: "Alice", extra: true });
// throws ZodError — unrecognized key "extra"

Can I generate JSON Schema from a Zod schema?

Yes. The zod-to-json-schema package converts any Zod schema to a JSON Schema object. This is useful when you need to share your schema with non-TypeScript consumers or OpenAPI documentation. You can also go the other direction: generate JSON Schema first using our JSON to JSON Schema tool, then convert it to Zod.

What is the performance overhead of Zod validation?

For typical payloads (objects with 10-50 fields), Zod validation takes microseconds and is negligible in the context of network requests or database calls. Validate at system boundaries — API handlers, form submissions, environment startup — rather than in hot inner loops. If you need to validate millions of records per second, consider Ajv with a precompiled JSON Schema, which is optimized for raw throughput.

Ready to generate your Zod schema?

Paste any JSON into our JSON to Zod Schema Generator and get production-ready TypeScript code instantly. Also check out the JSON to TypeScript and JSON to JSON Schema converters for related workflows.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

ZDJSON to Zod SchemaTSJSON to TypeScriptJSJSON to JSON Schema Generator{ }JSON FormatterJSON Validator

Related Articles

JSON Schema 유효성 검사: 타입, 도구, 모범 사례

JSON Schema 유효성 검사의 모든 것: 기본 타입부터 고급 패턴, 검증 라이브러리, TypeScript 및 API 통합까지.

TypeScript 제네릭 완전 가이드: 실전 예제로 배우기

기초부터 고급 패턴까지 TypeScript 제네릭을 마스터하세요. 함수, 인터페이스, 제약조건, 조건부 타입을 다룹니다.

Zod 검증 가이드: 스키마, 변환, Refinements, tRPC 통합

TypeScript Zod 스키마 검증 완전 가이드: 스키마, 변환, refinements, tRPC 통합.

JSON에서 TypeScript로: 예제와 함께하는 완벽 가이드

JSON 데이터를 TypeScript 인터페이스로 자동 변환하는 방법을 배웁니다. 중첩 객체, 배열, 선택적 필드, 모범 사례를 다룹니다.