Published on

How to Merge Multiple Zod Object Schemas: A Complete Guide

Authors
  • Name
    Ripal & Zalak
    Twitter

How to Merge Multiple Zod Object Schemas: A Complete Guide

Merging multiple Zod object schemas can be essential for building complex validation rules in your TypeScript applications. Whether you’re working with form validations, APIs, or custom logic, Zod provides powerful utilities for combining schemas effectively.

In this guide, we’ll explore several methods to merge Zod schemas, focusing on practical examples and best practices.


The Problem

You have three schemas for different types of blog posts:

  1. Image Post: Requires an image attachment and optional text.
  2. Video Post: Requires a video attachment and optional text.
  3. Text Post: Requires text only, with no attachments.

Here are the schemas:

import { z } from 'zod'

const imagePostSchema = z.object({
  body: z.string().max(500).optional().or(z.literal('')),
  attachmentType: z.literal('img'),
  attachment: z.string().url(), // Assume URL for simplicity
})

const videoPostSchema = z.object({
  body: z.string().max(500).optional().or(z.literal('')),
  attachmentType: z.literal('video'),
  attachment: z.string().url(), // Assume URL for simplicity
})

const textPostSchema = z.object({
  body: z.string().max(500),
  attachmentType: z.null(),
  attachment: z.null(),
})

Now, the goal is to merge these schemas into a single schema to allow users to submit any type of post.


Solution 1: Use z.union for Unions

The simplest way to merge schemas is by creating a union using z.union. This approach validates objects that match any one of the provided schemas.

Implementation:

const postSchema = z.union([imagePostSchema, videoPostSchema, textPostSchema])

Usage:

const result = postSchema.safeParse({
  body: 'Check out this image!',
  attachmentType: 'img',
  attachment: 'https://example.com/image.jpg',
})

console.log(result.success) // true if valid

Solution 2: Use z.discriminatedUnion for Discriminated Unions

If the schemas can be distinguished by a common field, such as attachmentType, you can use z.discriminatedUnion for better performance and type inference.

Implementation:

const postSchema = z.discriminatedUnion('attachmentType', [
  imagePostSchema,
  videoPostSchema,
  textPostSchema,
])

Why Use z.discriminatedUnion?

  • Performance: It only checks the schema corresponding to the attachmentType value.
  • Type Inference: TypeScript infers the correct type based on the discriminant.

Usage:

const result = postSchema.safeParse({
  body: 'Watch this video!',
  attachmentType: 'video',
  attachment: 'https://example.com/video.mp4',
})

if (result.success) {
  console.log(result.data.attachmentType) // "video"
}

Solution 3: Combine Schemas with z.object Spreading

If you want to merge all schemas into a single comprehensive schema, you can use the shape property to spread their fields.

Implementation:

const combinedSchema = z.object({
  ...imagePostSchema.shape,
  ...videoPostSchema.shape,
  ...textPostSchema.shape,
})

Use Case:

This approach is useful when the fields from all schemas need to coexist in one schema.

Drawback:

This doesn’t enforce mutual exclusivity between schema types. Use unions or discriminated unions for stricter validation.


Solution 4: Use z.intersection for Common Validations

If you need to combine schemas with overlapping validations, use z.intersection.

Implementation:

const sharedSchema = z.intersection(imagePostSchema, videoPostSchema)

Drawback:

This method works for AND logic (objects must satisfy both schemas) and is not suitable for mutually exclusive schemas like in this use case.


Best Practices

  1. Prefer z.discriminatedUnion: When a common field distinguishes schemas, it’s the most efficient and type-safe method.
  2. Avoid z.intersection for Unions: Use it only for overlapping fields with AND logic.
  3. Validate Early: Use safeParse or parse at data entry points to catch errors early.
  4. Leverage TypeScript: Type inference from Zod improves code reliability and developer experience.

Conclusion

Merging multiple Zod schemas depends on your specific requirements:

  • Use z.union for basic combinations.
  • Use z.discriminatedUnion for efficient validation with a shared field.
  • Use schema spreading for comprehensive schema creation.

By following these approaches, you can build robust and flexible validation logic for your applications.

Key Takeaways

  • z.union: Simple merging of schemas.
  • z.discriminatedUnion: Efficient validation with type inference.
  • Schema Spreading: Combine fields into one schema.
  • z.intersection: Combine schemas with overlapping validations.

With these techniques, you can manage complex validation scenarios effectively using Zod in TypeScript.