[{"data":1,"prerenderedAt":454},["ShallowReactive",2],{"blog-how-to/zod-validation":3},{"id":4,"title":5,"body":6,"category":435,"date":436,"dateModified":436,"description":437,"draft":438,"extension":439,"faq":440,"featured":438,"headerVariant":441,"image":440,"keywords":440,"meta":442,"navigation":443,"ogDescription":444,"ogTitle":440,"path":445,"readTime":440,"schemaOrg":446,"schemaType":447,"seo":448,"sitemap":449,"stem":450,"tags":451,"twitterCard":452,"__hash__":453},"blog/blog/how-to/zod-validation.md","How to Validate Input with Zod",{"type":7,"value":8,"toc":411},"minimark",[9,13,17,21,30,35,51,55,58,68,72,98,114,130,146,162,178,194,210,245,249,252,258,264,268,272,278,282,288,292,298,302,308,312,318,324,339,355,367,382,392],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-validate-input-with-zod",[18,19,20],"p",{},"TypeScript-first schema validation for secure, type-safe applications",[22,23,24,27],"tldr",{},[18,25,26],{},"TL;DR (20 minutes)",[18,28,29],{},"Zod is a TypeScript-first schema validation library. Define schemas once, use them for both validation and type inference. Use safeParse() for controlled error handling, refine() for custom validation logic, and transform() to normalize data. Integrate with React Hook Form for seamless form validation.",[31,32,34],"h2",{"id":33},"prerequisites","Prerequisites",[36,37,38,42,45,48],"ul",{},[39,40,41],"li",{},"Node.js 18+ installed",[39,43,44],{},"TypeScript project (Zod works with JS too, but you'll miss the best features)",[39,46,47],{},"Basic understanding of TypeScript types",[39,49,50],{},"npm or yarn package manager",[31,52,54],{"id":53},"why-zod","Why Zod?",[18,56,57],{},"Zod provides runtime validation that integrates seamlessly with TypeScript's type system. Unlike other validation libraries, you define your schema once and get both runtime validation AND compile-time types automatically.",[59,60,61],"info-box",{},[18,62,63,67],{},[64,65,66],"strong",{},"Zod vs. Other Libraries:"," Unlike Joi or Yup, Zod is TypeScript-first. You don't need to maintain separate type definitions - they're inferred from your schema. This eliminates the common bug of types not matching validation rules.",[31,69,71],{"id":70},"step-by-step-guide","Step-by-Step Guide",[73,74,76,81,84,95],"step",{"number":75},"1",[77,78,80],"h3",{"id":79},"install-zod","Install Zod",[18,82,83],{},"Add Zod to your project:",[85,86,91],"pre",{"className":87,"code":89,"language":90},[88],"language-text","npm install zod\n","text",[92,93,89],"code",{"__ignoreMap":94},"",[18,96,97],{},"That's it - Zod has zero dependencies and includes TypeScript types out of the box.",[73,99,101,105,108],{"number":100},"2",[77,102,104],{"id":103},"define-basic-schemas","Define basic schemas",[18,106,107],{},"Start with primitive types and build up to complex objects:",[85,109,112],{"className":110,"code":111,"language":90},[88],"import { z } from 'zod';\n\n// Primitive types\nconst stringSchema = z.string();\nconst numberSchema = z.number();\nconst booleanSchema = z.boolean();\nconst dateSchema = z.date();\n\n// With constraints\nconst emailSchema = z.string().email('Invalid email format');\nconst ageSchema = z.number().int().min(0).max(120);\nconst usernameSchema = z.string()\n  .min(3, 'Username must be at least 3 characters')\n  .max(20, 'Username must be 20 characters or less')\n  .regex(/^[a-zA-Z0-9_]+$/, 'Username can only contain letters, numbers, and underscores');\n\n// Object schemas\nconst UserSchema = z.object({\n  id: z.string().uuid(),\n  email: z.string().email(),\n  name: z.string().min(1).max(100),\n  age: z.number().int().positive().optional(),\n  role: z.enum(['user', 'admin', 'moderator']),\n  createdAt: z.date(),\n});\n\n// Infer TypeScript type from schema\ntype User = z.infer\u003Ctypeof UserSchema>;\n// Result: { id: string; email: string; name: string; age?: number; role: 'user' | 'admin' | 'moderator'; createdAt: Date; }\n\n// Array schemas\nconst TagsSchema = z.array(z.string()).min(1).max(10);\nconst UsersSchema = z.array(UserSchema);\n",[92,113,111],{"__ignoreMap":94},[73,115,117,121,124],{"number":116},"3",[77,118,120],{"id":119},"validate-data-with-safeparse","Validate data with safeParse",[18,122,123],{},"Use safeParse() for controlled error handling (recommended for APIs):",[85,125,128],{"className":126,"code":127,"language":90},[88],"import { z } from 'zod';\n\nconst LoginSchema = z.object({\n  email: z.string().email(),\n  password: z.string().min(8),\n});\n\n// Using safeParse (recommended - doesn't throw)\nfunction validateLogin(data: unknown) {\n  const result = LoginSchema.safeParse(data);\n\n  if (!result.success) {\n    // result.error contains structured error info\n    console.log(result.error.flatten());\n    // {\n    //   formErrors: [],\n    //   fieldErrors: {\n    //     email: ['Invalid email'],\n    //     password: ['String must contain at least 8 character(s)']\n    //   }\n    // }\n    return { valid: false, errors: result.error.flatten().fieldErrors };\n  }\n\n  // result.data is typed as { email: string; password: string }\n  return { valid: true, data: result.data };\n}\n\n// Using parse (throws on error - use in try/catch)\nfunction validateLoginThrowing(data: unknown) {\n  try {\n    const validated = LoginSchema.parse(data);\n    return validated; // Typed as { email: string; password: string }\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      console.error(error.issues);\n    }\n    throw error;\n  }\n}\n",[92,129,127],{"__ignoreMap":94},[73,131,133,137,140],{"number":132},"4",[77,134,136],{"id":135},"use-in-api-routes","Use in API routes",[18,138,139],{},"Validate request bodies in your API handlers:",[85,141,144],{"className":142,"code":143,"language":90},[88],"// lib/schemas/api.ts\nimport { z } from 'zod';\n\nexport const CreatePostSchema = z.object({\n  title: z.string()\n    .min(1, 'Title is required')\n    .max(200, 'Title must be 200 characters or less'),\n  content: z.string()\n    .min(10, 'Content must be at least 10 characters')\n    .max(50000, 'Content is too long'),\n  tags: z.array(z.string().min(1).max(30))\n    .min(1, 'At least one tag is required')\n    .max(5, 'Maximum 5 tags allowed'),\n  published: z.boolean().default(false),\n});\n\nexport type CreatePostInput = z.infer\u003Ctypeof CreatePostSchema>;\n\n// app/api/posts/route.ts\nimport { CreatePostSchema } from '@/lib/schemas/api';\nimport { getServerSession } from 'next-auth';\n\nexport async function POST(request: Request) {\n  const session = await getServerSession();\n  if (!session?.user) {\n    return Response.json({ error: 'Unauthorized' }, { status: 401 });\n  }\n\n  const body = await request.json();\n\n  // Validate request body\n  const result = CreatePostSchema.safeParse(body);\n\n  if (!result.success) {\n    return Response.json(\n      {\n        error: 'Validation failed',\n        details: result.error.flatten(),\n      },\n      { status: 400 }\n    );\n  }\n\n  // result.data is fully typed and validated\n  const post = await db.post.create({\n    data: {\n      ...result.data,\n      authorId: session.user.id,\n    },\n  });\n\n  return Response.json(post, { status: 201 });\n}\n",[92,145,143],{"__ignoreMap":94},[73,147,149,153,156],{"number":148},"5",[77,150,152],{"id":151},"add-custom-validation-with-refine","Add custom validation with refine",[18,154,155],{},"Use refine() for custom validation logic:",[85,157,160],{"className":158,"code":159,"language":90},[88],"import { z } from 'zod';\n\n// Single field refinement\nconst PasswordSchema = z.string()\n  .min(8, 'Password must be at least 8 characters')\n  .refine(\n    (password) => /[A-Z]/.test(password),\n    { message: 'Password must contain at least one uppercase letter' }\n  )\n  .refine(\n    (password) => /[a-z]/.test(password),\n    { message: 'Password must contain at least one lowercase letter' }\n  )\n  .refine(\n    (password) => /[0-9]/.test(password),\n    { message: 'Password must contain at least one number' }\n  )\n  .refine(\n    (password) => /[!@#$%^&*]/.test(password),\n    { message: 'Password must contain at least one special character (!@#$%^&*)' }\n  );\n\n// Cross-field validation with superRefine\nconst SignupSchema = z.object({\n  email: z.string().email(),\n  password: PasswordSchema,\n  confirmPassword: z.string(),\n}).refine(\n  (data) => data.password === data.confirmPassword,\n  {\n    message: 'Passwords do not match',\n    path: ['confirmPassword'], // Error appears on confirmPassword field\n  }\n);\n\n// Async validation (e.g., checking if email exists)\nconst UniqueEmailSchema = z.string().email().refine(\n  async (email) => {\n    const existingUser = await db.user.findUnique({ where: { email } });\n    return !existingUser;\n  },\n  { message: 'Email is already registered' }\n);\n\n// Use parseAsync for schemas with async refinements\nconst result = await UniqueEmailSchema.safeParseAsync('test@example.com');\n\n// Date range validation\nconst DateRangeSchema = z.object({\n  startDate: z.coerce.date(),\n  endDate: z.coerce.date(),\n}).refine(\n  (data) => data.endDate > data.startDate,\n  {\n    message: 'End date must be after start date',\n    path: ['endDate'],\n  }\n);\n",[92,161,159],{"__ignoreMap":94},[73,163,165,169,172],{"number":164},"6",[77,166,168],{"id":167},"transform-and-normalize-data","Transform and normalize data",[18,170,171],{},"Use transform() to clean and normalize input:",[85,173,176],{"className":174,"code":175,"language":90},[88],"import { z } from 'zod';\n\n// Normalize email to lowercase\nconst EmailSchema = z.string()\n  .email()\n  .transform((email) => email.toLowerCase().trim());\n\n// Parse string to number\nconst QueryParamSchema = z.object({\n  page: z.string().transform(Number).pipe(z.number().int().positive()),\n  limit: z.string().transform(Number).pipe(z.number().int().min(1).max(100)),\n});\n\n// Or use coerce for automatic type coercion\nconst CoercedQuerySchema = z.object({\n  page: z.coerce.number().int().positive().default(1),\n  limit: z.coerce.number().int().min(1).max(100).default(20),\n});\n\n// Clean and normalize user input\nconst UserInputSchema = z.object({\n  name: z.string()\n    .transform((s) => s.trim())\n    .pipe(z.string().min(1).max(100)),\n\n  bio: z.string()\n    .transform((s) => s.trim())\n    .transform((s) => s.replace(/\\s+/g, ' ')) // Collapse whitespace\n    .pipe(z.string().max(500))\n    .optional(),\n\n  website: z.string()\n    .transform((s) => {\n      s = s.trim();\n      if (s && !s.startsWith('http')) {\n        return `https://${s}`;\n      }\n      return s;\n    })\n    .pipe(z.string().url().optional().or(z.literal(''))),\n});\n\n// Preprocess for handling null/undefined\nconst OptionalStringSchema = z.preprocess(\n  (val) => (val === '' ? undefined : val),\n  z.string().min(1).optional()\n);\n",[92,177,175],{"__ignoreMap":94},[73,179,181,185,188],{"number":180},"7",[77,182,184],{"id":183},"integrate-with-react-hook-form","Integrate with React Hook Form",[18,186,187],{},"Use Zod with React Hook Form for type-safe form validation:",[85,189,192],{"className":190,"code":191,"language":90},[88],"// Install the resolver\n// npm install @hookform/resolvers\n\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\n\nconst ContactSchema = z.object({\n  name: z.string().min(1, 'Name is required').max(100),\n  email: z.string().email('Invalid email'),\n  message: z.string().min(10, 'Message must be at least 10 characters').max(1000),\n});\n\ntype ContactForm = z.infer\u003Ctypeof ContactSchema>;\n\nexport function ContactForm() {\n  const {\n    register,\n    handleSubmit,\n    formState: { errors, isSubmitting },\n    reset,\n  } = useForm\u003CContactForm>({\n    resolver: zodResolver(ContactSchema),\n    defaultValues: {\n      name: '',\n      email: '',\n      message: '',\n    },\n  });\n\n  const onSubmit = async (data: ContactForm) => {\n    // data is fully typed and validated\n    const response = await fetch('/api/contact', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(data),\n    });\n\n    if (response.ok) {\n      reset();\n    }\n  };\n\n  return (\n    \u003Cform onSubmit={handleSubmit(onSubmit)}>\n      \u003Cdiv>\n        \u003Clabel htmlFor=\"name\">Name\u003C/label>\n        \u003Cinput id=\"name\" {...register('name')} />\n        {errors.name && \u003Cspan className=\"error\">{errors.name.message}\u003C/span>}\n      \u003C/div>\n\n      \u003Cdiv>\n        \u003Clabel htmlFor=\"email\">Email\u003C/label>\n        \u003Cinput id=\"email\" type=\"email\" {...register('email')} />\n        {errors.email && \u003Cspan className=\"error\">{errors.email.message}\u003C/span>}\n      \u003C/div>\n\n      \u003Cdiv>\n        \u003Clabel htmlFor=\"message\">Message\u003C/label>\n        \u003Ctextarea id=\"message\" {...register('message')} />\n        {errors.message && \u003Cspan className=\"error\">{errors.message.message}\u003C/span>}\n      \u003C/div>\n\n      \u003Cbutton type=\"submit\" disabled={isSubmitting}>\n        {isSubmitting ? 'Sending...' : 'Send'}\n      \u003C/button>\n    \u003C/form>\n  );\n}\n",[92,193,191],{"__ignoreMap":94},[73,195,197,201,204],{"number":196},"8",[77,198,200],{"id":199},"build-reusable-schema-patterns","Build reusable schema patterns",[18,202,203],{},"Create reusable schemas for common patterns:",[85,205,208],{"className":206,"code":207,"language":90},[88],"// lib/schemas/common.ts\nimport { z } from 'zod';\n\n// Reusable field schemas\nexport const emailField = z.string().email().max(254).toLowerCase();\n\nexport const passwordField = z.string()\n  .min(8, 'Password must be at least 8 characters')\n  .max(100, 'Password is too long')\n  .regex(/[A-Z]/, 'Must contain uppercase letter')\n  .regex(/[a-z]/, 'Must contain lowercase letter')\n  .regex(/[0-9]/, 'Must contain number');\n\nexport const uuidField = z.string().uuid();\n\nexport const slugField = z.string()\n  .min(1)\n  .max(100)\n  .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Invalid slug format');\n\nexport const paginationSchema = z.object({\n  page: z.coerce.number().int().positive().default(1),\n  limit: z.coerce.number().int().min(1).max(100).default(20),\n  sortBy: z.string().optional(),\n  sortOrder: z.enum(['asc', 'desc']).default('desc'),\n});\n\n// API response wrapper\nexport function createApiResponseSchema\u003CT extends z.ZodTypeAny>(dataSchema: T) {\n  return z.object({\n    success: z.literal(true),\n    data: dataSchema,\n    meta: z.object({\n      timestamp: z.string().datetime(),\n      requestId: z.string(),\n    }).optional(),\n  });\n}\n\n// Paginated response\nexport function createPaginatedSchema\u003CT extends z.ZodTypeAny>(itemSchema: T) {\n  return z.object({\n    items: z.array(itemSchema),\n    pagination: z.object({\n      page: z.number(),\n      limit: z.number(),\n      total: z.number(),\n      totalPages: z.number(),\n    }),\n  });\n}\n\n// Usage\nconst UserSchema = z.object({\n  id: uuidField,\n  email: emailField,\n  name: z.string().min(1).max(100),\n});\n\nconst UsersResponseSchema = createPaginatedSchema(UserSchema);\ntype UsersResponse = z.infer\u003Ctypeof UsersResponseSchema>;\n",[92,209,207],{"__ignoreMap":94},[211,212,213,216],"warning-box",{},[18,214,215],{},"Security Checklist",[36,217,218,221,224,227,233,236,239,242],{},[39,219,220],{},"Always use server-side validation (client-side can be bypassed)",[39,222,223],{},"Use safeParse() instead of parse() in APIs for controlled error handling",[39,225,226],{},"Set maximum lengths on all string fields to prevent DoS",[39,228,229,230],{},"Use strict() to reject extra fields: ",[92,231,232],{},"z.object({...}).strict()",[39,234,235],{},"Validate enum values to prevent invalid states",[39,237,238],{},"Use coerce carefully - it may accept unexpected input types",[39,240,241],{},"Combine with sanitization for HTML/user content",[39,243,244],{},"Don't expose detailed validation errors for sensitive fields",[31,246,248],{"id":247},"how-to-verify-it-worked","How to Verify It Worked",[18,250,251],{},"Test your Zod validation:",[85,253,256],{"className":254,"code":255,"language":90},[88],"import { z } from 'zod';\n\n// Test your schemas\nconst UserSchema = z.object({\n  email: z.string().email(),\n  age: z.number().int().positive(),\n});\n\n// Valid data\nconst valid = UserSchema.safeParse({ email: 'test@example.com', age: 25 });\nconsole.assert(valid.success === true, 'Valid data should pass');\n\n// Invalid email\nconst invalidEmail = UserSchema.safeParse({ email: 'not-an-email', age: 25 });\nconsole.assert(invalidEmail.success === false, 'Invalid email should fail');\nconsole.assert(\n  invalidEmail.error?.flatten().fieldErrors.email?.length > 0,\n  'Should have email error'\n);\n\n// Wrong type\nconst wrongType = UserSchema.safeParse({ email: 'test@example.com', age: '25' });\nconsole.assert(wrongType.success === false, 'String age should fail');\n\n// Extra fields (use strict() to reject)\nconst StrictSchema = UserSchema.strict();\nconst extraFields = StrictSchema.safeParse({\n  email: 'test@example.com',\n  age: 25,\n  extra: 'field',\n});\nconsole.assert(extraFields.success === false, 'Extra fields should be rejected');\n\n// Test API endpoint\nconst testApiValidation = async () => {\n  // Missing required field\n  const response1 = await fetch('/api/users', {\n    method: 'POST',\n    body: JSON.stringify({ email: 'test@example.com' }), // Missing age\n  });\n  console.assert(response1.status === 400, 'Should return 400 for missing fields');\n\n  // Valid request\n  const response2 = await fetch('/api/users', {\n    method: 'POST',\n    body: JSON.stringify({ email: 'test@example.com', age: 25 }),\n  });\n  console.assert(response2.status === 201, 'Should return 201 for valid data');\n};\n\nconsole.log('All validation tests passed!');\n",[92,257,255],{"__ignoreMap":94},[259,260,261],"tip-box",{},[18,262,263],{},"Pro Tip:\nUse Zod's\n.describe()\nmethod to add documentation to schemas. Tools like zod-to-openapi can generate OpenAPI specs from your Zod schemas automatically.",[31,265,267],{"id":266},"common-errors-and-troubleshooting","Common Errors and Troubleshooting",[77,269,271],{"id":270},"error-type-inference-not-working","Error: Type inference not working",[85,273,276],{"className":274,"code":275,"language":90},[88],"// Problem: Using `any` or not inferring type properly\nconst schema = z.object({ name: z.string() });\nconst data: any = schema.parse(input); // Lost type safety!\n\n// Solution: Use z.infer\ntype MyType = z.infer\u003Ctypeof schema>;\nconst data: MyType = schema.parse(input);\n// Or let TypeScript infer:\nconst data = schema.parse(input); // Automatically typed\n",[92,277,275],{"__ignoreMap":94},[77,279,281],{"id":280},"error-async-validation-not-working","Error: Async validation not working",[85,283,286],{"className":284,"code":285,"language":90},[88],"// Problem: Using safeParse with async refinements\nconst schema = z.string().refine(async (val) => checkDatabase(val));\nconst result = schema.safeParse(input); // Won't work!\n\n// Solution: Use safeParseAsync for schemas with async refinements\nconst result = await schema.safeParseAsync(input);\n",[92,287,285],{"__ignoreMap":94},[77,289,291],{"id":290},"error-transform-changes-the-inferred-type-unexpectedly","Error: Transform changes the inferred type unexpectedly",[85,293,296],{"className":294,"code":295,"language":90},[88],"// Problem: Transform changes output type\nconst schema = z.string().transform(Number);\n// Input type: string, Output type: number\n\n// If you want to validate the transformed value:\nconst schema = z.string()\n  .transform(Number)\n  .pipe(z.number().positive()); // Validate the number\n\n// Or use coerce for simple type coercion:\nconst schema = z.coerce.number().positive();\n",[92,297,295],{"__ignoreMap":94},[77,299,301],{"id":300},"error-optional-vs-nullable-vs-nullish","Error: Optional vs nullable vs nullish",[85,303,306],{"className":304,"code":305,"language":90},[88],"// These all behave differently:\n\n// optional() - field can be missing OR undefined\nz.string().optional(); // string | undefined\n\n// nullable() - field can be null\nz.string().nullable(); // string | null\n\n// nullish() - field can be missing, undefined, OR null\nz.string().nullish(); // string | null | undefined\n\n// default() - provides a fallback value\nz.string().default(''); // Always returns string\n\n// Example of common confusion:\nconst schema = z.object({\n  name: z.string().optional(), // Can be omitted\n});\n\nschema.parse({}); // OK: { name: undefined }\nschema.parse({ name: null }); // ERROR: name must be string\nschema.parse({ name: 'Alice' }); // OK: { name: 'Alice' }\n",[92,307,305],{"__ignoreMap":94},[77,309,311],{"id":310},"error-discriminated-union-type-errors","Error: Discriminated union type errors",[85,313,316],{"className":314,"code":315,"language":90},[88],"// Problem: Union errors are confusing\nconst schema = z.union([\n  z.object({ type: z.literal('a'), value: z.string() }),\n  z.object({ type: z.literal('b'), value: z.number() }),\n]);\n\n// Solution: Use discriminatedUnion for better errors\nconst schema = z.discriminatedUnion('type', [\n  z.object({ type: z.literal('a'), value: z.string() }),\n  z.object({ type: z.literal('b'), value: z.number() }),\n]);\n// Now errors clearly indicate which variant failed based on 'type'\n",[92,317,315],{"__ignoreMap":94},[319,320,321],"faq-section",{},[18,322,323],{},"Frequently Asked Questions",[325,326,328],"faq-item",{"question":327},"Should I use parse() or safeParse()?",[18,329,330,331,334,335,338],{},"Use ",[92,332,333],{},"safeParse()"," in APIs and anywhere you need to handle errors gracefully. Use ",[92,336,337],{},"parse()"," when you want to throw on invalid data (e.g., internal code where invalid data indicates a bug). In production APIs, always use safeParse() to return proper error responses.",[325,340,342,349],{"question":341},"How do I validate environment variables with Zod?",[18,343,344,345,348],{},"Create an env schema and parse ",[92,346,347],{},"process.env"," at startup. This catches missing variables immediately:",[85,350,353],{"className":351,"code":352,"language":90},[88],"const envSchema = z.object({\n  DATABASE_URL: z.string().url(),\n  API_KEY: z.string().min(1),\n  PORT: z.coerce.number().default(3000),\n});\nexport const env = envSchema.parse(process.env);\n",[92,354,352],{"__ignoreMap":94},[325,356,358,361],{"question":357},"Can I use Zod for API response validation?",[18,359,360],{},"Yes! Validating API responses protects against unexpected data from third-party APIs:",[85,362,365],{"className":363,"code":364,"language":90},[88],"const response = await fetch('...');\nconst data = ResponseSchema.parse(await response.json());\n",[92,366,364],{"__ignoreMap":94},[325,368,370,376],{"question":369},"How do I handle recursive schemas?",[18,371,330,372,375],{},[92,373,374],{},"z.lazy()"," for recursive types:",[85,377,380],{"className":378,"code":379,"language":90},[88],"const CategorySchema: z.ZodType\u003CCategory> = z.object({\n  name: z.string(),\n  children: z.lazy(() => z.array(CategorySchema)),\n});\n",[92,381,379],{"__ignoreMap":94},[325,383,385],{"question":384},"Is Zod fast enough for production?",[18,386,387,388,391],{},"Yes. Zod is optimized for performance. For extremely high-throughput scenarios, consider caching parsed schemas or using ",[92,389,390],{},"z.preprocess()"," to skip validation for trusted internal data.",[393,394,395,401,406],"related-articles",{},[396,397],"related-card",{"description":398,"href":399,"title":400},"Complete form security with CSRF and honeypots","/blog/how-to/form-validation","Secure Form Validation",[396,402],{"description":403,"href":404,"title":405},"General input validation patterns","/blog/how-to/validate-user-input","Validate User Input",[396,407],{"description":408,"href":409,"title":410},"Combine validation with sanitization","/blog/how-to/sanitize-input","Sanitize User Input",{"title":94,"searchDepth":412,"depth":412,"links":413},2,[414,415,416,427,428],{"id":33,"depth":412,"text":34},{"id":53,"depth":412,"text":54},{"id":70,"depth":412,"text":71,"children":417},[418,420,421,422,423,424,425,426],{"id":79,"depth":419,"text":80},3,{"id":103,"depth":419,"text":104},{"id":119,"depth":419,"text":120},{"id":135,"depth":419,"text":136},{"id":151,"depth":419,"text":152},{"id":167,"depth":419,"text":168},{"id":183,"depth":419,"text":184},{"id":199,"depth":419,"text":200},{"id":247,"depth":412,"text":248},{"id":266,"depth":412,"text":267,"children":429},[430,431,432,433,434],{"id":270,"depth":419,"text":271},{"id":280,"depth":419,"text":281},{"id":290,"depth":419,"text":291},{"id":300,"depth":419,"text":301},{"id":310,"depth":419,"text":311},"how-to","2026-01-28","Step-by-step guide to input validation with Zod. Schema definition, API validation, form validation with React Hook Form, custom validators, and error handling.",false,"md",null,"yellow",{},true,"Master Zod for TypeScript validation. Schemas, transforms, refinements, and error handling.","/blog/how-to/zod-validation","[object Object]","HowTo",{"title":5,"description":437},{"loc":445},"blog/how-to/zod-validation",[],"summary_large_image","0YKnchxws7ihGMy1ATbssUa2cvQamNFMVumTEkEXAVI",1775843927137]