DEV Community

Jesica
Jesica

Posted on • Originally published at jestsee.com on

Setting Dynamic Default Values with Zod Schema

Introduction

Zod provides a powerful way to define schemas for validating and transforming data. You can easily define default values for properties.

but what if you need a default value based on another property in the schema?

Let’s explore how to handle that using Zod.

Simple Default Value

First, let’s see how we can define a simple default value for a property using Zod:

const schema = z.object({
  name: z.string().default('John Doe')
})

Enter fullscreen mode Exit fullscreen mode

In this example, the name property will have a default value of 'John Doe' if not supplied during parsing.

What If the Default Depends on Another Property?

Now, let’s consider a more complex case where the default value of one property depends on another. For instance, imagine we have a blog schema where the displayedTitle is the same as the title, but with a line break included for formatting purposes.

Here’s the basic schema:

const blogSchema = z.object({
  title: z.string(),
  displayedTitle: z.string()
})

Enter fullscreen mode Exit fullscreen mode

Next, let’s define a blogData object:

const blogData = {
  title: 'Bookmarked: Your Go-To Tool for Curating Tweets in Notion',
  displayedTitle: 'Bookmarked: Your Go-To Tool for\nCurating Tweets in Notion'
}

blogSchema.parse(blogData) // parsed successfully and returns blogData

Enter fullscreen mode Exit fullscreen mode

In the example above, both title and displayedTitle are provided. But what if we don’t want to define both title and displayedTitle manually, especially when they share the same value?

Conditional Defaults with transform

We can make the displayedTitle optional and derive it from title when necessary. This way, you only need to provide one value, and the other one is computed automatically.

Here’s how to update the schema:

const blogSchema = z
  .object({
    title: z.string(),
    displayedTitle: z.string().optional() // makes displayedTitle optional
  })
  .transform((data) => ({
    ...data,
    displayedTitle: data.displayedTitle || data.title // fallback to title if displayedTitle is undefined
  }))

Enter fullscreen mode Exit fullscreen mode

Now, if displayedTitle is not provided, it will fall back to using the title property.

For example:

const blogData = {
  title: 'This is the title without linebreak'
}

const parsedData = blogSchema.parse(blogData) // parsed successfully
console.log(parsedData)
// {
// title: "This is the title without linebreak",
// displayedTitle: "This is the title without linebreak"
// }

Enter fullscreen mode Exit fullscreen mode

As you can see, displayedTitle is derived from title automatically when it's missing.

Simplifying the Schema with Inferred Values

We can further simplify this by only defining the displayedTitle, and having title inferred automatically by transforming the value. For instance, you might want to replace line breaks (\n) in displayedTitle with spaces in title:

const blogSchema = z
  .object({
    displayedTitle: z.string()
  })
  .transform((data) => ({
    ...data,
    title: data.displayedTitle.replace('\\n', ' ') // removes the linebreaks from title
  }))

const blogData = {
  displayedTitle: 'This is the title\nwith linebreak'
}

const parsedData = blogSchema.parse(blogData) // parsed successfully
console.log(parsedData)
// {
// title: "This is the title with linebreak",
// displayedTitle: "This is the title\nwith linebreak"
// }

Enter fullscreen mode Exit fullscreen mode

Here, we’ve reduced the schema to only require displayedTitle, and the title is inferred automatically by removing the \n characters.

Conclusion

Zod allows you to create flexible schemas that validate data and derive properties based on other values. Its transform method offers an elegant solution for handling default values and derived properties efficiently.

Top comments (0)