Can Zod reshape and rename an incoming object?

428 Views Asked by At

I am trying to validate and reshape the following object:

{ title: "My title", body: "My body", category: { id: 1, name: "My category" }

I would like to output the following shape after validation:

{ title: "My title", body: "My body", categoryId: 1 }

Here is my schema:

const ArticleSchema = z.object({
  title: z.string().min(5),
  body: z.string().min(5),
  category: z
    .object({
      id: z.number().int(),
      name: z.string(),
    })
    .transform((input) => input.id),
});

Zod's transform method can modify the shape of the value, but I would also like to modify the name of the key to be categoryId instead of category.

ArticleSchema.parse(data) outputs this:

{ title: "My title", body: "My body", category: 1 }

I also tried this:

const ArticleSchema = z.object({
  title: z.string().min(5),
  body: z.string().min(5),
  category: z
    .object({
      id: z.number().int(),
      name: z.string(),
    })
    .transform((input) => ({ categoryId: input.id }),
});

But it outputs this:

{ title: "My title", body: "My body", category: { categoryId: 1 } }

Is this possible?

1

There are 1 best solutions below

0
On

This is possible, but it may lead to a good amount of duplication (if done in a naive way) since you cannot simply merge the values and add in the field you want without keeping the old field around.

What you are looking for can be achieved by moving the transform up a level to the outermost object. The categoryId key belongs to that object so that is the place you need to transform.

import { z } from "zod";
import { isEqual } from "lodash";

const input = { title: "My title", body: "My body", category: { id: 1, name: "My category" } };
const expected = { title: "My title", body: "My body", categoryId: 1 };

const schema = z.object({
  title: z.string().min(5),
  body: z.string().min(5),
  category: z.object({
    id: z.number().int(),
    name: z.string(),
  }),
}).transform((o) => ({
  title: o.title,
  body: o.body,
  categoryId: o.category.id,
}));

const parsed = schema.parse(input);

console.log(isEqual(parsed, expected)); // Logs true