7

I have implemented this code using JOI where the user has to send the userId and at least one of the keys from the body. How to implement the same using ZOD ??

  params: Joi.object().keys({
    userId: Joi.required().custom(objectId),
  }),
  body: Joi.object()
    .keys({
      name: Joi.string(),
      email: Joi.string().email(),
      password: Joi.string().custom(password),
    })
    .min(1),
};
Deepak Kumar
  • 73
  • 1
  • 1
  • 5

2 Answers2

12

I think there's no direct analog in zod but you can achieve this constraint using refine or superRefine

Using refine your schema might look like:

const schema = z.object({
  params: z.object({
    userId: z.string()
  }),
  body: z
    .object({
      name: z.string(),
      email: z.string().email(),
      password: z.string()
    })
    .partial()
    .refine(
      ({ name, email, password }) =>
        name !== undefined || email !== undefined || password !== undefined,
      { message: "One of the fields must be defined" }
    )
});

If the refine fails then you will get a ZodError with the message specified.

A more general refine function that could be reused on an arbitrary partial object would be something like:

const atLeastOneDefined = (obj: Record<string | number | symbol, unknown>) =>
  Object.values(obj).some(v => v !== undefined);

I noticed there was custom parsing for the object id and the password. I think those could both be done with refine as well but I'm not familiar with how Joi custom validators work so I didn't want to write them with refine and have it be incorrect code.

Souperman
  • 5,057
  • 1
  • 14
  • 39
-1

You may check this schema that will handle MongoDB id and custom password validation:

import mongoose from 'mongoose'
import { z } from 'zod'

const schema = z
  .object({
    params: z.object({
      userId: z.custom<mongoose.Types.ObjectId>(),
    }),
    body: z.object({
      name: z.string(),
      email: z.string().email(),
      password: z
        .string()
        .min(2, 'password should have at least 2 alphabets')
        .max(20, 'password should be no longer than 20 alphabets')
        .refine((value) => /[a-zA-Z]/.test(value), 'password should contain only alphabets')
     }),
   })
    .strict()
wael32gh
  • 324
  • 5
  • 11