| name | zod |
| description | Zod v4 schema patterns. For projects using Zod validation library. |
| triggers | zod, z\.object, z\.string, z\.number, z\.enum, z\.infer |
MCPSearch({ query: "select:mcp__plugin_devtools_context7__query-docs" })
// Zod v4 schema patterns
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use z.object, z.string, and z.number in Zod v4?",
});
// .meta() usage (v4 specific)
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use .meta() for description and examples in Zod v4?",
});
// Transforms and refinements
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use transform, refine, and superRefine?",
});
Note: Context7 v2 uses server-side filtering. Use descriptive natural language queries.
// ✅ CORRECT - Zod v4
z.string().meta({
description: "The recipient wallet address",
examples: ["0x1234567890123456789012345678901234567890"],
});
// ❌ WRONG - Zod v3 legacy
z.string().describe("The recipient wallet address");
Standard schema pattern:
import { z } from "zod";
export const mySchema = z
.string()
.min(1, "Must not be empty")
.max(100, "Must be at most 100 characters")
.transform((value) => value.toLowerCase())
.meta({
description: "Human-readable description for OpenAPI",
examples: ["valid-input"],
});
// Export inferred types
export type MySchema = z.infer<typeof mySchema>;
export type MySchemaInput = z.input<typeof mySchema>;
.pick(), .omit(), .extend() throw on refined schemas. Split base and refined:
// ✅ Correct - Split base and refined schemas
export const UserBaseSchema = z.object({
email: z.string().email(),
password: z.string(),
confirmPassword: z.string(),
});
export const UserSchema = UserBaseSchema.refine(
(data) => data.password === data.confirmPassword,
{ message: "Passwords must match", path: ["confirmPassword"] },
);
// Use base schema for composition
export const UserCreateSchema = UserBaseSchema.omit({ confirmPassword: true });
// ❌ Wrong - Will throw at runtime
// UserSchema.omit({ confirmPassword: true }); // Error!
Exclusive unions with z.xor() (v4.3+):
// z.xor() - fails if zero or more than one match
const PaymentMethod = z.xor([
z.object({ type: z.literal("card"), cardNumber: z.string() }),
z.object({ type: z.literal("bank"), accountNumber: z.string() }),
]);
Exact optional (v4.3+):
const Schema = z.object({
name: z.string(),
nickname: z.string().optional(), // accepts undefined
middleName: z.string().exactOptional(), // rejects undefined, allows omission
});
Schema.parse({ name: "Alice" }); // ✅
Schema.parse({ name: "Alice", nickname: undefined }); // ✅
Schema.parse({ name: "Alice", middleName: undefined }); // ❌
Composable transforms with .apply() (v4.3+):
const withPositiveConstraint = <T extends z.ZodNumber>(schema: T) => {
return schema.min(0).max(1_000_000);
};
const AmountSchema = z.number().apply(withPositiveConstraint).nullable();
const withTrimAndLowercase = <T extends z.ZodString>(schema: T) => {
return schema.trim().toLowerCase();
};
const EmailSchema = z.string().email().apply(withTrimAndLowercase);
Type predicates in refinements (v4.3+):
const AdminUser = z
.object({
role: z.string(),
permissions: z.array(z.string()),
})
.refine(
(user): user is { role: "admin"; permissions: string[] } =>
user.role === "admin",
);
type AdminOutput = z.output<typeof AdminUser>; // { role: "admin"; permissions: string[] }
Loose records (v4.3+):
// Only validates keys matching the pattern, passes through others
const EnvVars = z.looseRecord(z.string().regex(/^APP_/), z.string());
EnvVars.parse({ APP_NAME: "myapp", OTHER: 123 });
// ✅ { APP_NAME: "myapp", OTHER: 123 }
Enum with values:
export const AssetTypeValues = ["deposit", "bond", "equity"] as const;
export const AssetTypeEnum = z.enum(AssetTypeValues).meta({
description: "Type of tokenized asset",
examples: ["bond", "equity"],
});
export type AssetType = z.infer<typeof AssetTypeEnum>;
Required:
- Use
.meta({ description, examples })on all schemas - Export inferred types with
z.infer<> - Export input/output types for transforms:
z.input<>,z.output<> - Split base and refined schemas when using
.pick(),.omit(),.extend() - Use
z.xor()for exclusive unions (exactly one match required) - Use
.exactOptional()for properties that can be omitted but notundefined - Write tests for all schemas
Naming: Schema files=lowercase-with-hyphens.ts
- Context7 docs fetched for current v4 API
- Uses
.meta({ description, examples })(not.describe()) - Exports inferred types with
z.infer<> - Base and refined schemas split for composition
- Uses v4.3+ features:
z.xor(),.exactOptional(),.apply() - Has unit tests for valid/invalid cases