40.1 Deep schemas: nested objects and arrays

Overview and links for this section of the guide.

Nested Structures

Real-world data has nested objects. Your schemas should reflect this:

const OrderSchema = z.object({
  orderId: z.string(),
  customer: z.object({
    id: z.string(),
    name: z.string(),
    contact: z.object({
      email: z.string().email(),
      phone: z.string().optional()
    })
  }),
  items: z.array(z.object({
    productId: z.string(),
    quantity: z.number().int().positive(),
    price: z.object({
      unit: z.number(),
      total: z.number()
    })
  })),
  shipping: z.object({
    address: z.object({
      street: z.string(),
      city: z.string(),
      country: z.string(),
      postalCode: z.string()
    }),
    method: z.enum(['standard', 'express', 'overnight'])
  })
});

Array Handling

// Arrays with constraints
const TeamSchema = z.object({
  members: z.array(z.object({
    name: z.string(),
    role: z.enum(['lead', 'engineer', 'designer'])
  })).min(1).max(10),  // At least 1, at most 10
  
  // Tuple for fixed-length arrays
  coordinates: z.tuple([z.number(), z.number()]),
  
  // Non-empty arrays
  tags: z.array(z.string()).nonempty()
});

Prompting for Deep Schemas

function buildSchemaPrompt(schema: z.ZodType): string {
  return `
Output JSON matching this exact structure:
${JSON.stringify(zodToJsonSchema(schema), null, 2)}

Rules:
- Every required field must be present
- Use null for missing optional fields
- Arrays can be empty unless marked nonempty
- Enum fields must use exact values shown
`;
}

Where to go next