# Code Reference — Zod

_11 pages_

---
# Zod (/docs/zod)



# Zod [#zod]

Schema validation & TypeScript inference.

Browse the sidebar for every cheat sheet in this section.

## Sections [#sections]

*All notes are listed in the sidebar.*


---

# Arrays & Tuples (/docs/zod/arrays-tuples)



# Arrays & Tuples [#arrays--tuples]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Zod models homogeneous arrays with `z.array` and fixed-length positional collections with `z.tuple`. Useful for lists of DTOs and CSV-like rows.

## 🔧 Core concepts [#-core-concepts]

| Schema                      | Meaning                  |
| --------------------------- | ------------------------ |
| `z.array(T)`                | Array of T               |
| `z.tuple([A, B])`           | Fixed positions          |
| `.nonempty()`               | At least one element     |
| `.min` / `.max` / `.length` | Size constraints         |
| `z.record`                  | String-key map of values |
| `z.map` / `z.set`           | ES Map / Set             |

## 💡 Examples [#-examples]

**Array of objects:**

```ts
const Tag = z.string().min(1);
const Post = z.object({
  title: z.string(),
  tags: z.array(Tag).max(10),
});
```

**Tuple:**

```ts
const Pair = z.tuple([z.string(), z.number()]);
Pair.parse(["age", 30]);
```

**Nonempty:**

```ts
z.array(z.number()).nonempty().parse([1]);
```

## ⚠️ Pitfalls [#️-pitfalls]

* Tuples reject extra elements unless you configure rest (version-dependent).
* Sparse arrays and holes are unusual — prefer dense JSON arrays.
* Very large arrays: validation is O(n); validate early at the boundary.

## 🔗 Related [#-related]

* [objects.md](/docs/zod/objects)
* [primitives.md](/docs/zod/primitives)
* [refinements.md](/docs/zod/refinements)
* [safeparse.md](/docs/zod/safeparse)


---

# Coerce (/docs/zod/coerce)



# Coerce [#coerce]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`z.coerce` converts inputs before validation — useful for query strings, form fields, and env vars where everything arrives as strings.

## 🔧 Core concepts [#-core-concepts]

| Schema               | Coerces via                   |
| -------------------- | ----------------------------- |
| `z.coerce.string()`  | `String(value)`               |
| `z.coerce.number()`  | `Number(value)`               |
| `z.coerce.boolean()` | Boolean conversion (careful!) |
| `z.coerce.bigint()`  | BigInt                        |
| `z.coerce.date()`    | `new Date(value)`             |

| Good fit         | Risky fit                              |
| ---------------- | -------------------------------------- |
| `"42"` → number  | Boolean: `"false"` is truthy in JS     |
| ISO date strings | Invalid dates becoming Invalid Date    |
| Env PORT         | Objects coerced to `"[object Object]"` |

## 💡 Examples [#-examples]

**Query params:**

```ts
const Query = z.object({
  page: z.coerce.number().int().min(1).default(1),
  q: z.string().optional(),
});
Query.parse({ page: "2", q: "zod" });
```

**Env:**

```ts
const Env = z.object({
  PORT: z.coerce.number().default(9000),
  DEBUG: z.coerce.boolean().default(false),
});
```

**Dates:**

```ts
z.coerce.date().parse("2026-07-11T00:00:00.000Z");
```

## ⚠️ Pitfalls [#️-pitfalls]

* `z.coerce.boolean()` treats any non-empty string as `true` — including `"false"`. Prefer `z.enum(['true','false'])` + transform for forms.
* Coercion can hide bugs by accepting unexpected types.
* Prefer explicit transforms when conversion rules are domain-specific.

## 🔗 Related [#-related]

* [primitives.md](/docs/zod/primitives)
* [transforms.md](/docs/zod/transforms)
* [safeparse.md](/docs/zod/safeparse)
* [getting\_started.md](/docs/zod/getting-started)


---

# Getting Started with Zod (/docs/zod/getting-started)



# Getting Started with Zod [#getting-started-with-zod]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Zod is a TypeScript-first schema library for parsing and validating data at runtime while inferring static types. Common for API payloads, env config, and forms.

## 🔧 Core concepts [#-core-concepts]

| Idea       | Meaning                                  |
| ---------- | ---------------------------------------- |
| Schema     | Declarative description of a value shape |
| Parse      | Validate + return typed data (or throw)  |
| Safe parse | Validate without throwing                |
| Infer      | Extract TS type from a schema            |
| Transform  | Map input → output during parse          |

**Install:**

```shellscript
npm install zod
```

| Entry   | Import                               |
| ------- | ------------------------------------ |
| Zod 3   | `import \{ z \} from "zod"`          |
| Schemas | `z.string()`, `z.object(\{...\})`, … |

## 💡 Examples [#-examples]

**First schema:**

```ts
import { z } from "zod";

const User = z.object({
  email: z.string().email(),
  age: z.number().int().positive(),
});

const user = User.parse({ email: "a@b.co", age: 30 });
```

**Type inference:**

```ts
type User = z.infer<typeof User>;
```

**Env-style parse:**

```ts
const Env = z.object({ PORT: z.coerce.number().default(9000) });
const env = Env.parse(process.env);
```

## ⚠️ Pitfalls [#️-pitfalls]

* `parse` throws `ZodError` — use `safeParse` at trust boundaries if you prefer Result-style control flow.
* Zod validates runtime values; it does not replace compile-time TS checking alone.
* Major version APIs differ (Zod 3 vs 4) — match docs to your installed version.

## 🔗 Related [#-related]

* [primitives.md](/docs/zod/primitives)
* [objects.md](/docs/zod/objects)
* [safeparse.md](/docs/zod/safeparse)
* [infer\_types.md](/docs/zod/infer-types)
* [glossary.md](/docs/zod/glossary)


---

# Glossary (/docs/zod/glossary)



# Glossary [#glossary]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Alphabetical glossary of Zod schema, parsing, and type-inference terms.

## 🔧 Core concepts [#-core-concepts]

| Term                | Definition                                                |
| ------------------- | --------------------------------------------------------- |
| Coercion            | Converting input types before validation (`z.coerce`).    |
| Discriminated union | `z.discriminatedUnion` selecting a schema by a tag field. |
| Effect              | Pipeline step such as transform, refine, or preprocess.   |
| Infer               | Extracting a TypeScript type from a schema (`z.infer`).   |
| Issue               | Single validation problem inside a `ZodError`.            |
| Literal             | Schema matching one exact value.                          |
| Parse               | Validate and return data, throwing on failure.            |
| Pipe                | Connect schema output into another schema.                |
| Refine              | Custom predicate adding constraints beyond built-ins.     |
| Safe parse          | Non-throwing parse returning a result object.             |
| Schema              | Runtime validator object created by Zod builders.         |
| Strip               | Default object behavior removing unknown keys.            |
| Transform           | Map a validated value to a new output.                    |
| Tuple               | Fixed-length array schema with per-index types.           |
| ZodError            | Error class aggregating validation issues.                |

## 💡 Examples [#-examples]

**Discriminated union sketch:**

```ts
const Event = z.discriminatedUnion("type", [
  z.object({ type: z.literal("click"), x: z.number() }),
  z.object({ type: z.literal("nav"), href: z.string() }),
]);
```

**Result narrowing:**

```ts
const r = Event.safeParse(input);
if (r.success) console.log(r.data.type);
```

## ⚠️ Pitfalls [#️-pitfalls]

* Glossary terms may differ slightly across Zod major versions.
* `z.any()` disables safety — prefer `z.unknown()` then narrow.

## 🔗 Related [#-related]

* [getting\_started.md](/docs/zod/getting-started)
* [safeparse.md](/docs/zod/safeparse)
* [infer\_types.md](/docs/zod/infer-types)
* [README.md](/docs/zod)


---

# Infer Types (/docs/zod/infer-types)



# Infer Types [#infer-types]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Zod schemas are a single source of truth: validate at runtime and derive TypeScript types with `z.infer`, `z.input`, and `z.output`.

## 🔧 Core concepts [#-core-concepts]

| Helper               | Yields                     |
| -------------------- | -------------------------- |
| `z.infer<typeof S>`  | Output type (usual choice) |
| `z.input<typeof S>`  | Type before transforms     |
| `z.output<typeof S>` | Type after transforms      |
| `z.infer` on objects | Nested object types        |

| Pattern           | Benefit                               |
| ----------------- | ------------------------------------- |
| Schema-first      | No drift between types and validators |
| Shared DTO module | API + client agree                    |
| Env schema        | Typed `process.env`                   |

## 💡 Examples [#-examples]

**Basic infer:**

```ts
const User = z.object({
  id: z.string(),
  email: z.string().email(),
});
type User = z.infer<typeof User>;
// { id: string; email: string }
```

**Input vs output:**

```ts
const S = z.string().transform((s) => s.length);
type In = z.input<typeof S>;   // string
type Out = z.output<typeof S>; // number
```

**Function boundary:**

```ts
function save(user: z.infer<typeof User>) {
  /* ... */
}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Don't hand-write a duplicate `interface` that can drift — infer from the schema.
* Optional fields (`?`) vs `| undefined` differ slightly in TS excess property checks.
* Circular types need `z.lazy` — plain infer won't recurse infinitely by itself.

## 🔗 Related [#-related]

* [transforms.md](/docs/zod/transforms)
* [objects.md](/docs/zod/objects)
* [safeparse.md](/docs/zod/safeparse)
* [getting\_started.md](/docs/zod/getting-started)


---

# Objects (/docs/zod/objects)



# Objects [#objects]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`z.object` defines keyed shapes with required/optional fields, nested objects, and helpers for picking, omitting, extending, and strictness.

## 🔧 Core concepts [#-core-concepts]

| API                 | Effect                      |
| ------------------- | --------------------------- |
| `z.object(\{...\})` | Define shape                |
| `.partial()`        | All keys optional           |
| `.required()`       | All keys required           |
| `.pick` / `.omit`   | Subset keys                 |
| `.extend`           | Add fields                  |
| `.merge`            | Combine objects             |
| `.strict()`         | Reject unknown keys         |
| `.passthrough()`    | Keep unknown keys           |
| `.strip()`          | Drop unknown keys (default) |

## 💡 Examples [#-examples]

**Nested object:**

```ts
const Address = z.object({
  city: z.string(),
  zip: z.string().min(3),
});

const Person = z.object({
  name: z.string(),
  address: Address,
});
```

**Strict vs strip:**

```ts
z.object({ id: z.string() }).strict().parse({ id: "1", extra: true });
// throws — unknown key
```

**Pick / extend:**

```ts
const User = z.object({
  id: z.string(),
  email: z.string().email(),
  password: z.string(),
});
const PublicUser = User.omit({ password: true });
const Admin = User.extend({ role: z.literal("admin") });
```

## ⚠️ Pitfalls [#️-pitfalls]

* Default object schemas strip unknown keys — fine for DTOs, surprising for proxies.
* `.optional()` on a field allows missing key; distinguish from `.nullable()`.
* Deep partials need care — `.partial()` is shallow unless you build recursively.

## 🔗 Related [#-related]

* [primitives.md](/docs/zod/primitives)
* [arrays\_tuples.md](/docs/zod/arrays-tuples)
* [infer\_types.md](/docs/zod/infer-types)
* [transforms.md](/docs/zod/transforms)


---

# Primitives (/docs/zod/primitives)



# Primitives [#primitives]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Zod provides schemas for JS primitives and common string/number refinements (email, URL, int, min/max, etc.).

## 🔧 Core concepts [#-core-concepts]

| Schema                       | Validates                                       |
| ---------------------------- | ----------------------------------------------- |
| `z.string()`                 | string                                          |
| `z.number()`                 | number (not `NaN` by default in typical setups) |
| `z.boolean()`                | boolean                                         |
| `z.bigint()`                 | bigint                                          |
| `z.date()`                   | Date instance                                   |
| `z.undefined()` / `z.null()` | exact undefined / null                          |
| `z.any()` / `z.unknown()`    | anything / unknown                              |
| `z.literal(x)`               | exact value                                     |
| `z.enum([...])`              | string union                                    |

| String helpers              | Example           |
| --------------------------- | ----------------- |
| `.min` / `.max` / `.length` | Length bounds     |
| `.email` / `.url` / `.uuid` | Formats           |
| `.regex`                    | Custom pattern    |
| `.trim` / `.toLowerCase`    | String transforms |

## 💡 Examples [#-examples]

**Strings and numbers:**

```ts
const Password = z.string().min(8).max(72);
const Port = z.number().int().min(1).max(65535);
```

**Literals and enums:**

```ts
const Role = z.enum(["admin", "user"]);
const Ok = z.literal("ok");
```

**Optional / nullable:**

```ts
z.string().optional(); // string | undefined
z.string().nullable(); // string | null
z.string().nullish();  // string | null | undefined
```

## ⚠️ Pitfalls [#️-pitfalls]

* JSON has no `Date` — use `z.coerce.date()` or ISO strings + transform.
* Empty string is still a string; use `.min(1)` or `.trim().min(1)` for required text.
* `z.number()` rejects numeric strings unless you coerce.

## 🔗 Related [#-related]

* [objects.md](/docs/zod/objects)
* [coerce.md](/docs/zod/coerce)
* [refinements.md](/docs/zod/refinements)
* [getting\_started.md](/docs/zod/getting-started)


---

# Refinements (/docs/zod/refinements)



# Refinements [#refinements]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Refinements add custom predicates and cross-field checks that built-in validators cannot express — passwords match, date ranges, XOR fields, etc.

## 🔧 Core concepts [#-core-concepts]

| API                    | Role                               |
| ---------------------- | ---------------------------------- |
| `.refine(fn, message)` | Extra boolean check                |
| `.superRefine`         | Multiple issues via `ctx.addIssue` |
| `.refine` on objects   | Cross-field validation             |
| Second arg             | Custom error message / path        |

| Pattern              | Example use                      |
| -------------------- | -------------------------------- |
| Confirm password     | `password === confirm`           |
| Conditional required | If `type==='card'` require `cvv` |
| Business rules       | Unique SKU format                |

## 💡 Examples [#-examples]

**Simple refine:**

```ts
const Even = z.number().refine((n) => n % 2 === 0, {
  message: "Expected even number",
});
```

**Cross-field:**

```ts
const Signup = z
  .object({
    password: z.string().min(8),
    confirm: z.string(),
  })
  .refine((d) => d.password === d.confirm, {
    message: "Passwords must match",
    path: ["confirm"],
  });
```

**superRefine:**

```ts
z.string().superRefine((val, ctx) => {
  if (val.includes(" ")) {
    ctx.addIssue({ code: "custom", message: "No spaces" });
  }
});
```

## ⚠️ Pitfalls [#️-pitfalls]

* Refinements run after base parsing — coercions/transforms order matters.
* Forgetting `path` makes UI error mapping harder.
* Heavy async refinements (DB uniqueness) belong in an app layer; keep schemas fast when possible (Zod supports async refine carefully).

## 🔗 Related [#-related]

* [transforms.md](/docs/zod/transforms)
* [objects.md](/docs/zod/objects)
* [safeparse.md](/docs/zod/safeparse)
* [primitives.md](/docs/zod/primitives)


---

# safeParse (/docs/zod/safeparse)



# safeParse [#safeparse]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`safeParse` returns a discriminated result instead of throwing. Prefer it at HTTP/form boundaries where invalid input is expected.

## 🔧 Core concepts [#-core-concepts]

| API                                    | Behavior                                        |
| -------------------------------------- | ----------------------------------------------- |
| `schema.parse(data)`                   | Returns value or throws `ZodError`              |
| `schema.safeParse(data)`               | `\{ success, data \}` or `\{ success, error \}` |
| `schema.parseAsync` / `safeParseAsync` | Async refinements/transforms                    |
| `error.flatten()`                      | Field errors for forms                          |
| `error.format()`                       | Nested error tree                               |

## 💡 Examples [#-examples]

**Branch on success:**

```ts
const result = User.safeParse(payload);
if (!result.success) {
  console.error(result.error.flatten());
  return;
}
console.log(result.data.email);
```

**HTTP handler style:**

```ts
const parsed = Body.safeParse(await req.json());
if (!parsed.success) {
  return Response.json(parsed.error.flatten(), { status: 400 });
}
```

**flatten for UI:**

```ts
const { fieldErrors, formErrors } = error.flatten();
```

## ⚠️ Pitfalls [#️-pitfalls]

* After `success === true`, TypeScript narrows `data` — don't access `data` on the failure branch.
* Swallowing `error` without logging loses diagnostics.
* `parse` in a hot request path without try/catch can crash the process if unhandled.

## 🔗 Related [#-related]

* [getting\_started.md](/docs/zod/getting-started)
* [refinements.md](/docs/zod/refinements)
* [objects.md](/docs/zod/objects)
* [infer\_types.md](/docs/zod/infer-types)


---

# Transforms (/docs/zod/transforms)



# Transforms [#transforms]

*Zod · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Transforms change values during parsing: trim strings, normalize casing, map DTOs, or convert types. Output types can differ from input types.

## 🔧 Core concepts [#-core-concepts]

| API              | Role                                       |
| ---------------- | ------------------------------------------ |
| `.transform(fn)` | Map value after validation                 |
| `.pipe(schema)`  | Feed output into another schema            |
| `z.preprocess`   | Run before validation                      |
| Input vs output  | `z.input<typeof S>` / `z.output<typeof S>` |

| Use         | Example                |
| ----------- | ---------------------- |
| Normalize   | trim + lowercase email |
| Defaults    | fill missing fields    |
| DTO mapping | rename keys            |

## 💡 Examples [#-examples]

**Trim email:**

```ts
const Email = z
  .string()
  .trim()
  .toLowerCase()
  .email();
```

**Transform shape:**

```ts
const Raw = z.object({ user_id: z.string() }).transform((d) => ({
  userId: d.user_id,
}));
```

**Pipe:**

```ts
const IsoDate = z.string().datetime().pipe(z.coerce.date());
```

## ⚠️ Pitfalls [#️-pitfalls]

* Transforms run on success path — failed refinements skip later transforms.
* Inferred `z.infer` is the *output* type; use `z.input` when typing forms that submit raw values.
* Overusing preprocess can hide invalid data instead of failing loudly.

## 🔗 Related [#-related]

* [coerce.md](/docs/zod/coerce)
* [infer\_types.md](/docs/zod/infer-types)
* [refinements.md](/docs/zod/refinements)
* [objects.md](/docs/zod/objects)

