Zod - TypeScript-first schema declaration and validation library #10
Nhan Nguyen
Posted on December 15, 2023
✨ Reduce Duplicated Code by Composing Schemas ✨
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:
- Medium: https://medium.com/@nhannguyendevjs/
- Dev: https://dev.to/nhannguyendevjs/
- Hashnode: https://nhannguyen.hashnode.dev/
- Linkedin: https://www.linkedin.com/in/nhannguyendevjs/
- X (formerly Twitter): https://twitter.com/nhannguyendevjs/
Posted on December 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
December 28, 2023
December 17, 2023