Application of Criteria for Zod Depending on Data Stored in Array Field

I am currently working on an Express route that requires validation of the request body using Zod. The challenge arises when I need to conditionally require certain fields based on the values in the "channels" field, which is an array of enums. While my current implementation works, I find it a bit verbose and would appreciate any suggestions for a more concise approach. Despite exploring options like discriminated union, I have not found a solution that efficiently handles arrays of values.

Here is the logic:

  • The "userId" and "channels" fields are always mandatory.
  • If "channels" includes "EMAIL", then email-related fields should also be required.
  • If "channels" includes "SMS", then sms-related fields should be required.
  • If "channels" includes "FEED", then feed-related fields should be required.

This is how I currently handle it:

const schema = z.object({
  userId: z.string(),
  channels: z.array(z.enum(["EMAIL", "SMS", "FEED"])).nonempty(),
});

// Definition of emailSchema, smsSchema, feedSchema...

const result = schema.safeParse(req.body);

if (!result.success) {
  // Error handling...
}

let channelsSchema = z.object({});

if (result.data.channels.includes("EMAIL"))
  channelsSchema = channelsSchema.merge(emailSchema);
if (result.data.channels.includes("SMS"))
  channelsSchema = channelsSchema.merge(smsSchema);
if (result.data.channels.includes("FEED"))
  channelsSchema = channelsSchema.merge(feedSchema);

const channelsResult = channelsSchema.safeParse(req.body);

if (!channelsResult.success) {
  // More error handling...
}

// Processing the validated data...

I have come across examples involving refine and superRefine, but these seem even more complex. Any insights or alternative solutions would be greatly appreciated!

Thank you for your help!

Answer №1

Approach #1: Utilizing Unions

One approach is to employ zod.refine in order to ensure that each type of message contains the correct channel flag, followed by using zod.union to generate a union of all message types. Zod will make an attempt to parse each schema within the union and will only raise an error if there are no matches found.

For instance:

const emailSchema = z.object({
  userId: z.string(),
  channels: z.array(z.string()).refine((val) => val.includes('EMAIL')),
  // More schema properties for emails
});

// Define schemas for SMS and feeds as well

const schema = z.union([
  emailSchema,
  smsSchema,
  feedSchema,
]);

Approach #2: Custom Schema Selection

Alternatively, you could create custom logic to determine the appropriate schema based on the values present in the channels array.

const messageSchema = z.object({
  userId: z.string(),
  channels: z.array(z.string()),
});

// Extend messageSchema to include specific properties for emails, SMS, and feeds 

function validateMessage(data: object) {
  const message = messageSchema.parse(data);
  
  if (message.channels.includes('EMAIL')) return emailSchema.parse(data);
  // Logic for selecting the right schema based on channel type
}

Comments

It's worth noting that using an array as the field to indicate the shape of the object might seem unconventional. Typically, a number or string field is utilized for denoting an object's type to simplify the design.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Drawing a personalized cursor using JavaScript on a canvas

I've been working on creating a canvas that allows for drawing in vanilla JavaScript. Although I've successfully enabled drawing on the canvas, I'm now looking to implement a feature where a preview dot shows up when the user presses down be ...

Using the Selenium webdriver to reach the bottom of the page by scrolling vertically

Is there a way to determine if I have scrolled to the bottom of the page vertically? I have been using Webdriver and pressing the page down key repeatedly within a for loop, like so: for(int di=0; di<40; di++) { driver.findElement(By.tagName("body ...

What are some steps I can take to diagnose why my Express server is not receiving POST requests from my HTML form?

Struggling with an unexpected issue on my website where the form submission is not triggering the POST request to my Express server. I've set up a MongoDB database and created a HTML form to store user data, but something seems to be amiss. HTML: & ...

JQuery implementation of Axis webservice call

Trying to invoke my Axis webservice with the code below var wsUrl = "http://localhost:8080/TestServ/services/TestCls?wsdl"; var soapreq = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:cod=\"http:// ...

Using the spread operator in Typescript with an object that contains methods

Within my Angular project, I am faced with an object that includes a type and status field where the status can change depending on the type. While some might argue that this is not the best design practice, it is how things are currently structured in my ...

How to vertically align Material UI ListItemSecondaryAction in a ListItem

I encountered an issue with @material-ui/core while trying to create a ListItem with Action. I am looking for a way to ensure that the ListItemSecondaryAction stays on top like ListItemAvatar when the secondary text becomes longer. Is there any solution to ...

What is the best way to incorporate template literals (` `) into existing template literals?

I am facing a unique challenge where I need to utilize a template literal within another template literal, but I am struggling to make it work. The code snippet in question looks like this: <p>Something something <a href={`${SOMELINK}/blah`}> ...

Unable to click on link with JavaScript using Selenium webdriver

<a id="compareCompanies" b:onclick="needsController.showQuotes = true;" href="#">Compare companies</a> Below is the Selenium Webdriver JavaScript code using Mocha: driver.wait(function () { driver.findElement(webdriver.By.id("compareCompa ...

jquery to create a fading effect for individual list items

I have a group of items listed, and I would like them to smoothly fade out while the next one fades in seamlessly. Below is the code I've been working on: document.ready(function(){ var list_slideshow = $("#site_slideshow_inner_text"); ...

Using Python Webdriver to Execute JavaScript File and Passing Arguments to Functions

How can I execute a JavaScript function and pass arguments to it? value = driver.execute_script(open("path/file.js").read()) I have successfully executed the file, but I am unsure of how to pass arguments to the function within it. Any suggestions would ...

JavaScript can dynamically attach EventListeners to elements, allowing for versatile and customized event

I am currently populating a table using data from an XML file. One of the columns in the table contains links to more details. Due to the unique requirements of my web page setup (Chrome extension), I need to dynamically add an event handler when the table ...

Tips on troubleshooting the issue when attempting to use a hook in your code

I am trying to implement a hook to manage the states and event functions of my menus. However, when I try to import the click function in this component, I encounter the following error: "No overload matches this call. The first of two overloads, '(p ...

How can I apply red border styling only after the first click in MUI React?

I am currently working with MUI Textfields and I have a specific styling requirement. I would like the field to display a red outline only after the first click, if the input is left blank or deleted. Essentially, I want the field to appear normal initiall ...

Incorporating AJAX functionality into an existing PHP form

I am currently working on a PHP registration form that validates user inputs using $_POST[] requests. Validating username length (3-20 characters) Checking username availability Ensuring the username matches /^[A-Za-z0-9_]+$/ pattern and more... Instead ...

Pulling JSON Data with Ajax Request

I am attempting to retrieve the following JSON Data: {"status":"success","id":8,"title":"Test","content":"This is test 12"} Using this Ajax Request: $.ajax({ url: 'http://www.XXX.de/?apikey=XXX&search=test', type: "GET", dataType: 'js ...

Creating a login page with Titanium Appelerator is a breeze

Looking for guidance on creating a login page using Titanium Appcelerator docs. Struggling to grasp the documentation - any recommendations for tutorials on storing user data in a database, accessing it, and implementing a login system? ...

Navigating through an array in Angular 5: a guide

This question may seem simple, but I'm having trouble figuring it out. Here is the code snippet I am working with: getInstabul(): void { this.homeService.getWeather() .subscribe((weather) => { this.instanbulWeathers = weather ...

Accessing a specific element within an array that has been nested within another array, using JavaScript

Here's what my code looks like: planets[0]= new THREE.Mesh( geometry, material ); planettexts[0]= new THREE.Mesh( textGeometry, textMaterial ); planets[0].add(planettexts[0]); Now, I am trying to hide the planettext, but every attempt results in an ...

Encountering an error stating "cloudinary.uploader is undefined" while attempting to delete media files from Cloudinary

I'm currently developing a web application using express and node.js. In my project, I'm utilizing cloudinary for uploading media files. While uploading and accessing media works smoothly, I'm encountering an issue with deleting images from ...

Tips on sending a function's return value to an object in Node.js

I'm currently exploring Node.js and working on a REST API for a project. One of the functionalities I am implementing is a post request to store form data in a database. Some values will be retrieved from the form data while others will be generated ...