DEV Community

Nhan Nguyen
Nhan Nguyen

Posted on • Edited on

3

Zod - TypeScript-first schema declaration and validation library #10

Image description

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(),
})


Enter fullscreen mode Exit fullscreen mode

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(),
})


Enter fullscreen mode Exit fullscreen mode

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(),
})


Enter fullscreen mode Exit fullscreen mode

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(),
})


Enter fullscreen mode Exit fullscreen mode

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(),
})


Enter fullscreen mode Exit fullscreen mode

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(),
  }),
)


Enter fullscreen mode Exit fullscreen mode

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:

Postgres on Neon - Get the Free Plan

No credit card required. The database you love, on a serverless platform designed to help you build faster.

Get Postgres on Neon

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay