Photo by Amy Humphries on Unsplash
Setting Dynamic Default Values with Zod Schema
Leverage Zod's powerful features to set dynamic default values and enhance data validation with flexibility and ease.
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')
})
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()
})
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
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
}))
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"
// }
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"
// }
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.