✨ 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/
 

    
Top comments (0)