install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/zod-object-patterns" ~/.claude/skills/intense-visions-harness-engineering-zod-object-patterns && rm -rf "$T"
manifest:
agents/skills/claude-code/zod-object-patterns/SKILL.mdsource content
Zod Object Patterns
Shape and compose Zod objects with pick, omit, partial, required, extend, merge, strict, and passthrough
When to Use
- Deriving a subset schema from an existing schema (e.g., update payload excludes
)id - Making all or some fields optional for PATCH endpoints
- Extending a base schema with additional fields without re-definition
- Controlling whether unknown keys are stripped, passed through, or rejected
Instructions
- Use
to create a schema with only specified keys:.pick()
import { z } from 'zod'; const UserSchema = z.object({ id: z.string().uuid(), name: z.string(), email: z.string().email(), role: z.enum(['admin', 'viewer']), createdAt: z.date(), }); // Only name and email — for public-facing profile display const PublicProfileSchema = UserSchema.pick({ name: true, email: true }); type PublicProfile = z.infer<typeof PublicProfileSchema>; // { name: string; email: string }
- Use
to exclude specific keys:.omit()
// Create user input — exclude server-generated fields const CreateUserSchema = UserSchema.omit({ id: true, createdAt: true }); type CreateUserInput = z.infer<typeof CreateUserSchema>; // { name: string; email: string; role: 'admin' | 'viewer' }
- Use
to make all fields optional (for PATCH/update payloads):.partial()
// Full partial — all fields optional const UpdateUserSchema = UserSchema.partial(); // Selective partial — only some fields optional const PatchUserSchema = UserSchema.partial({ name: true, role: true }); // id and email remain required
- Use
to make optional fields required again:.required()
const DraftSchema = z.object({ title: z.string().optional(), body: z.string().optional(), publishedAt: z.date().optional(), }); // Publish requires all fields const PublishSchema = DraftSchema.required();
- Use
to add new fields to an existing schema:.extend()
const BaseEntitySchema = z.object({ id: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), }); const PostSchema = BaseEntitySchema.extend({ title: z.string().min(1), content: z.string(), authorId: z.string().uuid(), tags: z.array(z.string()).default([]), });
- Use
to combine two object schemas (right side wins on key conflicts):.merge()
const TimestampsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), deletedAt: z.date().nullable(), }); const ArticleSchema = z .object({ title: z.string(), content: z.string(), }) .merge(TimestampsSchema);
- Control unknown key handling — default is strip (silently remove unknown keys):
const StrictUserSchema = UserSchema.strict(); // Throws if input has keys not in the schema const PassthroughSchema = UserSchema.passthrough(); // Passes unknown keys through to the output unchanged const StrippedSchema = UserSchema.strip(); // default behavior — explicit for clarity
- Access the shape of a schema for programmatic inspection:
const keys = Object.keys(UserSchema.shape); // ['id', 'name', 'email', 'role', 'createdAt'] const emailSchema = UserSchema.shape.email; // z.ZodString
Details
vs .extend()
:.merge()
Both add fields to an object schema. The difference:
accepts a plain object of Zod types (more ergonomic, handles most cases).extend()
accepts a full.merge()
(needed when combining two existing schema variables)ZodObject
// extend — preferred when adding known fields const WithMetadata = UserSchema.extend({ metadata: z.record(z.string()) }); // merge — preferred when combining two schema variables const Combined = SchemaA.merge(SchemaB);
Deep partial:
Zod's
.partial() is shallow — nested objects remain fully required. For deep partial, recurse manually or use a utility:
function deepPartial<T extends z.ZodRawShape>(schema: z.ZodObject<T>) { const entries = Object.entries(schema.shape).map(([key, val]) => { if (val instanceof z.ZodObject) { return [key, deepPartial(val).optional()]; } return [key, (val as z.ZodTypeAny).optional()]; }); return z.object(Object.fromEntries(entries)); }
Deriving CRUD schemas from one base:
const TaskSchema = z.object({ id: z.string().uuid(), title: z.string().min(1), description: z.string().optional(), priority: z.enum(['low', 'medium', 'high']), dueDate: z.date().optional(), completedAt: z.date().nullable(), }); export const CreateTaskSchema = TaskSchema.omit({ id: true, completedAt: true }); export const UpdateTaskSchema = TaskSchema.partial().required({ id: true }); export const TaskResponseSchema = TaskSchema; export type Task = z.infer<typeof TaskSchema>; export type CreateTaskInput = z.infer<typeof CreateTaskSchema>; export type UpdateTaskInput = z.infer<typeof UpdateTaskSchema>;
Source
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.