@tank/zod-patterns
1.0.0Description
Zod validation patterns covering schema composition, transforms, refinements, error handling, type inference, coercion, Zod 4 features, and integrations with React Hook Form, tRPC, Next.js, and Express/Hono.
Triggered by
zodzod schemavalidationinfertransformrefine
Download
Verified
tank install @tank/zod-patternsZod Patterns
Core Philosophy
- Parse, don't validate — Use
safeParseto convert unknown data into typed values at system boundaries. Parsing returns typed data; validation just says yes/no. - Schema is the source of truth — Define the Zod schema first, infer TypeScript types from it with
z.infer<>. Never duplicate types manually. - Fail at the boundary, trust the interior — Validate untrusted data (API inputs, form data, env vars, config files) at entry points. Once parsed, the interior code operates on trusted types without re-validation.
- Compose, don't repeat — Build complex schemas by composing primitives:
.extend(),.pick(),.omit(),.partial(),.merge(), spread syntax. Reuse base schemas across endpoints. - Errors are data, not exceptions — Prefer
safeParseoverparsein application code. Reserve throwingparsefor scripts and CLIs where crash-on-invalid is acceptable.
Quick-Start: Common Problems
"How do I validate an API request body?"
- Define a Zod schema for the expected shape
- Call
schema.safeParse(req.body)in the handler - Return 400 with formatted errors if
!result.success - Use
result.data(fully typed) for business logic -> Seereferences/api-validation.md
"How do I connect Zod with React Hook Form?"
- Install
@hookform/resolvers - Pass
zodResolver(schema)touseForm({ resolver: ... }) - Infer form types from the schema with
z.infer<typeof schema>-> Seereferences/form-integration.md
"How do I validate environment variables?"
const envSchema = z.object({
DATABASE_URL: z.url(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "staging", "production"]),
DEBUG: z.stringbool().default("false"),
});
export const env = envSchema.parse(process.env);
-> See references/practical-recipes.md
"How do I transform data during parsing?"
- Use
.transform()to change the output type - Use
.pipe()to chain schema-to-schema transformations - Use
.overwrite()(Zod 4) for same-type mutations that preserve introspection -> Seereferences/transforms-pipes.md
"Which validation library should I use?"
| Need | Library |
|---|---|
| TypeScript-first, largest ecosystem | Zod |
| Smallest bundle size (<1kb) | Valibot |
| Fastest runtime validation | ArkType |
| Legacy projects, Formik | Yup |
-> See references/ecosystem-comparison.md |
Decision Trees
Schema Composition
| Goal | Method |
|---|---|
| Add fields to object | .extend() or spread { ...A.shape, ...B.shape } |
| Remove fields | .omit({ key: true }) |
| Keep only specific fields | .pick({ key: true }) |
| Make all fields optional | .partial() |
| Make all fields required | .required() |
| Combine two object schemas | z.intersection(A, B) or spread |
Error Handling
| Context | Approach |
|---|---|
| API handlers, middleware | safeParse + return formatted errors |
| CLI scripts, build tools | parse (throw on failure) |
| Forms (React Hook Form) | zodResolver handles errors automatically |
| Global error messages | z.config({ customError: ... }) |
| Per-field custom messages | z.string({ error: "..." }) |
Parse vs SafeParse
| Signal | Use |
|---|---|
| Untrusted external input (API, form, file) | safeParse — handle errors gracefully |
| Startup config, env vars (must succeed) | parse — crash fast if invalid |
| Internal data you trust | Skip validation entirely |
Reference Index
| File | Contents |
|---|---|
references/schema-primitives.md | All schema types: strings, numbers, objects, arrays, enums, unions, discriminatedUnion, records, tuples, literals, dates, nullish, recursive schemas |
references/transforms-pipes.md | Transforms, pipes, coercion, preprocess, overwrite, refinements, superRefine, custom validators, async validation |
references/error-handling.md | safeParse, ZodError structure, error customization (schema-level, per-parse, global), error formatting, prettifyError, i18n locales |
references/type-inference.md | z.infer, z.input, z.output, branded types, readonly schemas, JSON Schema conversion, metadata registries, Zod Mini |
references/form-integration.md | React Hook Form + zodResolver, server actions, progressive enhancement, conditional validation, field arrays, multi-step forms |
references/api-validation.md | Express/Hono/Next.js middleware, tRPC integration, request/response validation, OpenAPI generation |
references/practical-recipes.md | Environment variables, config parsing, API response validation, date handling, file uploads, discriminated union patterns |
references/ecosystem-comparison.md | Zod vs Valibot vs ArkType vs Yup vs TypeBox, bundle size, performance benchmarks, migration paths, Zod 3 to 4 migration |