question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Refine validations on object definitions don't get triggered until all fields in the object exist.

See original GitHub issue

Applies to zod v3. I have not tried the same scenario on older versions.

I have two dates, startDate and endDate and I want to verify that the former is always earlier than the latter. I tried doing something like this:

z.object({
  name: z.string(),
  startDate: z.date(),
  endDate: z.date(),
})
.refine((data) => checkValidDates(data.startDate, data.endDate), 'Start date must be earlier than End date.')

The problem is that the .refine() function defined on the object type doesn’t get triggered until the all fields in the object are defined. So, imagine you have a form and user entered both startDate and endDate first, but in a wrong order. At this point, the validations for these dates do not trigger because user has not entered name. Then, user goes ahead enters name and user now sees the validations for the dates kicking in. This is very awkward experience.

I can see that in this issue #61, the example adds a .partial() to get around this problem, but this really isn’t a solution because all of these fields are required in my case. It’d be nice if the .refine function defined on each individual field had access to the current form values, but it has access to its own value only. Any ideas, or did I miss something?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:12
  • Comments:19

github_iconTop GitHub Comments

15reactions
ekawatanicommented, Jun 2, 2021

Let me reiterate the issue in more detail.

It may be by design that refine validations on a z.object do not trigger until all fields in the object are filled in. For example, this is probably desired behavior when validating a password and a password confirmation. In this case, we’d expect the refine validations to trigger once both fields are filled in. However, this is only true if those are the only fields being validated. If this example had several more fields, then we still want to trigger the validation as soon as those two fields are filled in, not when all fields are provided.

One solution is to create a nested object schema and use refine on that. e.g.

z.object({
  name: z.string,
  dates: z.object({
    startDate: z.date(),
    endDate: z.date()
  })
  .refine((data) => checkValidDates(data.startDate, data.endDate), 'Start date must be earlier than End date.'),
})

Sure, this works, but what if there’s another field that depends on start and end dates? In my scenario, the start and end date are the dates of an event. I also have a due date field which specifies when an event registration is due. So, I need to make sure it’s earlier than the start date. Now, I need to wait until start and end dates are both filled in before using a refine validation. But, what I want is to trigger the validation as soon as start and due date are filled in. zod cannot currently handle this if I’m correct. Also, doing this breaks the structure, so it doesn’t play nice with Blitz.js.

My suggestion is to have something like yup.when which gives you access to values of other fields. To enable this, the refine function may also give you back a function that retrieves the value of a specified field. For example:

z.object({
  name: z.string,
  startDate: z
    .date()
    .refine((value, getField) => checkValidDates(value, getField("endDate")), 'Start date must be earlier than End date.'),
  endDate: z
    .date()
    .refine((value, getField) => checkValidDates(getField("startDate"), value), 'Start date must be earlier than End date.'),
  dueDate: z
    .date()
    .refine((value, getField) => checkValidDates(value, getField("startDate")), 'Due date must be earlier than Start date.'),
})

This is my suggestion. By moving the refine into the field schema, then we can makes sure it gets triggered as soon as that field is modified. There’s also no need to create a nested object schema. If there’s an existing solution, then that’s great. Please let met know.

I hope I made my point clear enough. I’m not sure if this is technically possible with how zod is written, but this feels like such a basic scenario, so it’d be great support this.

10reactions
gcourtemanchecommented, Sep 14, 2021

I’m currently in the process of testing Zod to migrate from Yup. I love it so far, but the issue is also an irritant for us

Read more comments on GitHub >

github_iconTop Results From Across the Web

CWE-20: Improper Input Validation (4.9) - MITRE
Input validation is a frequently-used technique for checking potentially dangerous inputs in order to ensure that the inputs are safe for ...
Read more >
Why does Zod make all my schema fields optional?
I am using Zod inside my Express & TypeScript & Mongoose API project and when trying to validate my user input against ...
Read more >
Jakarta Bean Validation specification
Constraint declaration and validation process describes how a JavaBean class is decorated with annotations to describe constraints.
Read more >
Bean Validation specification
This document is the specification of the Java API for JavaBean validation in Java EE and Java SE. The technical objective of this...
Read more >
Database Engine events and errors - SQL Server
Consult this MSSQL error code list to find explanations for error messages for SQL Server database engine events.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found