We will be looking at techniques for refactoring working code to remove duplication.
Here we have schemas for User, Post, and Comment:
const User = z.object({
id: z.string().uuid(),
name: z.string(),
})
const Post = z.object({
id: z.string().uuid(),
title: z.string(),
body: z.string(),
})
const Comment = z.object({
id: z.string().uuid(),
text: z.string(),
})
Notice that id is present in each.
Zod provides us with various ways of composing objects together into different types, allowing us to DRY out our code.
👉 Challenge:
We will use Zod to refactor the above code to remove the id duplication.
👉 Solution:
There are a bunch of ways that this code could be refactored.
For reference, here is what we started with:
const User = z.object({
id: z.string().uuid(),
name: z.string(),
})
const Post = z.object({
id: z.string().uuid(),
title: z.string(),
body: z.string(),
})
const Comment = z.object({
id: z.string().uuid(),
text: z.string(),
})
➖ The Simple solution:
The simplest solution is to strip out the id into its type. From there, each of the z.objects could reference it:
const Id = z.string().uuid();
const User = z.object({
id: Id,
name: z.string(),
})
const Post = z.object({
id: Id,
title: z.string(),
body: z.string(),
})
const Comment = z.object({
id: Id,
text: z.string(),
})
This is pretty good, but id: ID is still being repeated. All of the cases are still passing, so that is okay.
➖ Use the Extend Method:
Another solution would be to create a base object called ObjectWithId. This base object will contain our id:
const ObjectWithId = z.object({
id: z.string().uuid(),
})
From there, we can use the extend method to create new schemas that add on to the base object:
const ObjectWithId = z.object({
id: z.string().uuid(),
})
const User = ObjectWithId.extend({
name: z.string(),
})
const Post = ObjectWithId.extend({
title: z.string(),
body: z.string(),
})
const Comment = ObjectWithId.extend({
text: z.string(),
})
Note that .extend() will overwrite fields!
➖ Use the Merge Method:
Similar to the above solution, we could use the merge method to extend the ObjectWithId base object:
const User = ObjectWithId.merge(
z.object({
name: z.string(),
}),
)
Using .merge() is slightly more verbose than .extend(). We have to pass in a z.object() that contains the name z.string().
Merging is generally used when two different types are being combined, rather than just extending a single type.
⭐ Summary:
Those are a few different ways that you can compose objects together in Zod to reduce the amount of code duplication, make things more DRY, and make things a bit more maintainable!
I hope you found it useful. Thanks for reading. 🙏
Let’s get connected! You can find me on:
Hashnode: https://nhannguyen.hashnode.dev/
X (formerly Twitter): https://twitter.com/nhannguyendevjs/