zod


Custom Validation

Custom Validation in Node.js Zod

1. Overview

Zod is a library that helps you validate data in Node.js. Custom validation allows you to define your own validation rules beyond the built-in ones.

2. Creating a Custom Validator

To create a custom validator, use the z.custom function:

const customValidator = z.custom((value) => {
  // Perform your validation logic here
  // Return true if valid, false if invalid
});

3. Using a Custom Validator

Once you have created a custom validator, you can use it in your schema:

const schema = z.object({
  name: z.string(),
  email: z.string().email(),
  age: customValidator
});

4. Example: Validating a Social Security Number

Let's create a custom validator to ensure that a social security number (SSN) is in the correct format:

const ssnValidator = z.custom((ssn) => {
  // Check if the SSN is a valid format
  const validFormat = /^\d{3}-\d{2}-\d{4}$/.test(ssn);
  
  // Check if the SSN is not all zeros
  const notAllZeros = ssn !== '000-00-0000';
  
  // Return true if both checks pass
  return validFormat && notAllZeros;
});

const schema = z.object({
  ssn: ssnValidator
});

5. Real-World Applications

Custom validation is useful when you need to:

  • Validate data that does not fit into the built-in schema types (e.g., SSNs, phone numbers)

  • Enforce business logic (e.g., checking if a user is old enough)

6. Additional Tips

  • You can use the safe modifier to ensure that the custom validator does not throw errors (instead, it returns a validation error object).

  • You can create nested custom validators using the z.lazy() function.

  • To learn more, refer to the Zod documentation on custom validation: https://github.com/colinhacks/zod/blob/main/docs/guides/custom-validation.md


Schema Parsing

Schema Parsing

Schema parsing is the process of checking if a value matches a predefined set of rules or specifications. In the context of Node.js, Zod provides a library for schema parsing that helps you validate user input, database queries, and other data.

Building a Schema

To create a schema, you use the zod library:

const { z } = require("zod");

Simple Schema

A simple schema validates only the type of value. For example:

const userSchema = z.object({
  name: z.string(), // The name must be a string.
  age: z.number(), // The age must be a number.
});

Nested Schema

Nested schemas allow you to validate complex data structures:

const addressSchema = z.object({
  street: z.string(),
  city: z.string(),
  state: z.string(),
  zip: z.string(),
});

const userSchema = z.object({
  name: z.string(),
  age: z.number(),
  address: addressSchema, // The address must match the addressSchema.
});

Validating Data

Once you have a schema, you can validate data against it:

const data = {
  name: "John Doe",
  age: 30,
  address: {
    street: "123 Main Street",
    city: "Anytown",
    state: "CA",
    zip: "12345",
  },
};

try {
  userSchema.parse(data);
  console.log("Data is valid");
} catch (error) {
  console.error("Data is invalid:", error.errors);
}

This will print "Data is valid" to the console because the data matches the schema.

Potential Applications

Schema parsing has many applications in real-world software development, including:

  • Validating user input in web forms or API requests.

  • Ensuring data integrity in databases.

  • Parsing configuration files or other structured data.

  • Enforcing consistent data formats in distributed systems.


Schema Serialization

Schema Serialization in Node.js Zod

What is Schema Serialization?

Schema serialization is the process of converting a Zod schema into a string representation for storage or sharing. This allows you to easily recreate the schema from the string later on.

Benefits of Schema Serialization:

  • Preservation: Store and retrieve schemas across different environments or teams.

  • Collaboration: Share schemas with others for consistency and validation.

  • Testing: Generate test cases based on serialized schemas.

How to Serialize a Schema:

You can serialize a Zod schema using the .serialize() method:

const schema = z.string().min(3);
const serializedSchema = schema.serialize();

The serializedSchema will be a string representation of the original schema.

How to Deserialize a Schema:

To recreate a schema from its serialized representation, use the z.schema() function:

const deserializedSchema = z.schema(serializedSchema);

The deserializedSchema will be a new Zod schema equivalent to the original one.

Applications in the Real World:

Database Migration:

  • Create automated schema migration scripts by serializing and deserializing schemas between versions.

API Contract Validation:

  • Share serialized schemas with API consumers to ensure consistent request and response validation.

Testing:

  • Generate test cases based on serialized schemas to ensure data validation logic is accurate.

Example Code Implementation:

// Store a serialized schema in a database
const db = require("database");
const serializedSchema = schema.serialize();

db.save("schemas", { name: "userSchema", schema: serializedSchema });

// Retrieve and deserialize a schema from the database
const retrievedSchema = db.get("schemas", { name: "userSchema" });
const deserializedSchema = z.schema(retrievedSchema.schema);

Conclusion:

Schema serialization is a powerful tool in Zod that allows you to easily preserve, share, and recreate schemas. This enables various applications in database migration, API contract validation, and testing.


Object Types

Object Types

What is an object type?

An object type is a way to represent a group of related data in a single data structure. For example, you could create an object type to represent a person, and include properties for their name, age, and address.

Why use object types?

Object types can help you to:

  • Organize your data in a logical way

  • Make your code more readable and maintainable

  • Validate your data to ensure that it is in the correct format

Creating an object type

To create an object type, you can use the z.object() function. This function takes an object as an argument, where the keys of the object represent the property names of the object type, and the values of the object represent the types of the properties.

For example, the following code creates an object type to represent a person:

const PersonType = z.object({
  name: z.string(),
  age: z.number(),
  address: z.string(),
});

Using an object type

Once you have created an object type, you can use it to validate data. To do this, you can use the parse() function. The parse() function takes two arguments: the data to be validated, and the object type to be used for validation.

For example, the following code validates a data object using the PersonType object type:

const data = {
  name: "John Doe",
  age: 30,
  address: "123 Main Street",
};

const result = PersonType.parse(data);

If the data is valid, the parse() function will return the validated data. Otherwise, the parse() function will throw an error.

Real-world examples

Object types can be used in a variety of real-world applications, such as:

  • Data validation: Object types can be used to validate data from forms, APIs, and other sources.

  • Data modeling: Object types can be used to model data for databases, object-oriented programming, and other applications.

  • Data serialization: Object types can be used to serialize data into JSON, XML, or other formats.

Here are some specific examples of how object types can be used in these applications:

Data validation:

const userSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  password: z.string().min(8),
});

const user = {
  name: "John Doe",
  email: "johndoe@example.com",
  password: "password",
};

const result = userSchema.parse(user);

In this example, the userSchema object type is used to validate a user object. The parse() function will throw an error if the user object is invalid.

Data modeling:

class Person {
  constructor(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
  }
}

const person = new Person("John Doe", 30, "123 Main Street");

In this example, the Person class is used to model a person object. The constructor function takes three arguments: the person's name, age, and address. The person object can then be used to access the person's data.

Data serialization:

const data = {
  name: "John Doe",
  age: 30,
  address: "123 Main Street",
};

const json = JSON.stringify(data);

In this example, the JSON.stringify() function is used to serialize the data object into JSON. The JSON string can then be used to store the data in a database, transmit it over a network, or use it in other applications.


Validation Errors

Validation Errors in Node.js Zod

Overview

Zod is a library that helps you validate and parse data in Node.js. When a validation fails, Zod provides detailed error messages to help you identify and fix the issue.

Understanding the Validation Errors

Zod errors are divided into two types:

  1. Type Errors: These errors occur when the data type does not match the expected type. For example, if you expect a number but receive a string.

  2. Custom Errors: These errors are thrown when you define your own validation rules and one of them fails.

Each error includes the following information:

  • Code: A unique code representing the error type.

  • Message: A human-readable explanation of the error.

  • Path: The path to the specific data field that failed validation.

Simplifying Validation Errors

To make validation errors easier to understand, Zod provides the following methods:

  • error.flatten(): Returns a flat object with all the errors and their paths.

  • error.format(): Formats the error messages in a user-friendly way.

Code Snippets

// Type Error
const result = zod.number().parse('abc');
console.log(result.error.message); // Output: "Expected a number, but got a string"

// Custom Error
const userSchema = zod.object({
  name: zod.string(),
  age: zod.number().positive(),
});

try {
  const user = userSchema.parse({ name: 'John', age: -10 });
} catch (error) {
  console.log(error.error.format()); // Output: "Data does not match the schema: { age: 'Value must be positive' }"
}

Real-World Applications

Validation errors are essential for:

  • Ensuring data consistency and integrity.

  • Providing meaningful feedback to users when submitting invalid data.

  • Improving the user experience by preventing errors from being stored in the database or processed further.

Potential Applications

  • Form Validation: Validating user input in web forms to prevent invalid entries.

  • API Request Validation: Ensuring that API requests have the correct data structure and format.

  • Data Parsing: Extracting and cleaning data from various sources, such as CSV files or JSON responses.


Schema Definition

Schema Definition

A schema in Zod is a set of rules that define the structure and validation of data. It's like a blueprint for your data, ensuring that it meets certain criteria before it's accepted.

1. Primitive Types

Primitive types are the most basic data types that Zod can handle, such as strings, numbers, and booleans. Examples:

const schema = z.string();
const result = schema.parse("Hello, world!"); // Valid
const schema = z.number();
const result = schema.parse(123); // Valid
const schema = z.boolean();
const result = schema.parse(true); // Valid

2. Arrays

Arrays hold a list of values of the same type. Examples:

const schema = z.array(z.string());
const result = schema.parse(["apple", "banana", "cherry"]); // Valid

3. Objects

Objects have named properties with specific types. Examples:

const schema = z.object({
  name: z.string(),
  age: z.number(),
});
const result = schema.parse({ name: "John", age: 30 }); // Valid

4. Unions

Unions allow a value to be one of multiple possible types. Examples:

const schema = z.union([z.string(), z.number()]);
const result = schema.parse("Hello"); // Valid
const result2 = schema.parse(123); // Also valid

5. Custom Shapes

Custom shapes allow you to define your own complex data structures. Examples:

const Person = z.object({
  name: z.string(),
  age: z.number().min(18), // Validate that age is at least 18
});
const result = Person.parse({ name: "John", age: 25 }); // Valid
const result2 = Person.parse({ name: "Jane", age: 15 }); // Invalid (age is under 18)

Real-World Applications:

  • API Request Validation: Ensure that incoming API requests conform to a specific structure and contain the correct data types.

  • Form Data Validation: Check that form submissions meet certain criteria, such as email addresses being valid and passwords meeting complexity requirements.

  • Data Consistency: Maintain consistency across different parts of your application by ensuring that data adheres to defined schemas.

  • Error Handling: Catch data validation errors early on to prevent unexpected behavior and improve user experience.


Integration with Express

Integration with Express

1. Middleware

A middleware is a function that processes a request before passing it to the next middleware or route handler. Zod provides a middleware function that can be used to validate the request body, query parameters, or request headers.

Example:

const express = require('express');
const zod = require('zod');

const app = express();

const userSchema = zod.object({
  name: zod.string(),
  email: zod.string().email(),
});

const userMiddleware = (req, res, next) => {
  try {
    const user = userSchema.parse(req.body);
    req.user = user;
    next();
  } catch (error) {
    res.status(400).json({ error: error.errors });
  }
};

app.use(userMiddleware);

app.post('/users', (req, res) => {
  const user = req.user;
  // The user is guaranteed to be valid at this point
  res.json(user);
});

2. Schema TypeGuards

Schema typeguards are functions that check if a value is valid for a given schema. They can be used to check the validity of request body, query parameters, or request headers in a type-safe manner.

Example:

const express = require('express');
const zod = require('zod');

const app = express();

const userSchema = zod.object({
  name: zod.string(),
  email: zod.string().email(),
});

app.post('/users', (req, res) => {
  const user = req.body;
  if (userSchema.safeParse(user).success) {
    // The user is valid
    res.json(user);
  } else {
    res.status(400).json({ error: 'Invalid user data' });
  }
});

3. Real-World Applications

  • API Validation: Validate the input and output of RESTful APIs to ensure data integrity.

  • Form Validation: Validate user input from forms to prevent malicious or invalid data from being submitted.

  • Schema Enforcement: Enforce specific data structures and formats for objects and data in your application.


Optional Properties

Optional Properties

What are optional properties?

Optional properties are properties that do not have to be provided when creating an object. They can be useful for properties that may not apply to all objects, or that may not be available when creating the object.

How to create optional properties?

To create an optional property, you can use the ? operator after the property name. For example:

const user = {
  name: 'John',
  age: 30,
  location?: 'New York' // Optional property
};

Accessing optional properties

To access an optional property, you can use the ? operator after the property name. For example:

console.log(user.location); // New York

If the optional property is not set, the ? operator will return undefined. For example:

const user2 = {
  name: 'Jane',
  age: 25
};

console.log(user2.location); // undefined

Default values for optional properties

You can provide a default value for an optional property using the ?? operator. For example:

const user3 = {
  name: 'John',
  age: 30,
  location: null // Optional property with default value
};

console.log(user3.location ?? 'Unknown'); // Unknown

Real-world examples

Optional properties can be useful in a variety of real-world applications, such as:

  • Representing objects with variable data: For example, a user object may have an optional address property that is only set if the user has provided their address.

  • Modeling complex data structures: Optional properties can be used to represent nested objects and arrays, which can make it easier to manage complex data.

  • Providing default values for missing data: Optional properties with default values can ensure that your code does not break when missing data is encountered.

Potential applications

  • User profiles

  • Product catalogs

  • Data analysis

  • Configuration files

  • API responses


Validation Error Handling

Validation Error Handling in Node.js with Zod

What is Validation Error Handling?

Validation error handling is the process of catching and handling errors that occur when validating data using a validation library like Zod. This helps ensure that your application is receiving and processing valid data.

Simplifying Zod's Error Handling

Zod's error handling is straightforward:

  • Validation Result: After validation, Zod returns a validation result object.

  • isSuccess: If the validation is successful, isSuccess is true.

  • error: If there are any errors, error is a ZodError object.

Handling Validation Errors

1. Using the try...catch Block:

try {
  const result = zodSchema.parse(data);
} catch (error) {
  // Handle the ZodError as needed
}

2. Using the .then() and .catch() Methods:

zodSchema
  .parse(data)
  .then((result) => {
    // Validation successful
  })
  .catch((error) => {
    // Handle the ZodError as needed
  });

3. Using the async/await Syntax:

const result = await zodSchema.parse(data);
if (result.error) {
  // Handle the ZodError as needed
}

Real-World Example

Consider an API endpoint that accepts user registration data. We can use Zod to validate the incoming data before saving it to the database.

async function registerUser(req, res) {
  const user = req.body;

  const userSchema = z.object({
    name: z.string().min(3),
    email: z.string().email(),
    password: z.string().min(8),
  });

  try {
    const result = userSchema.parse(user);
    // Validation successful, save user to database
  } catch (error) {
    res.status(400).json({
      error: "Validation failed",
      details: error.errors,
    });
  }
}

Conclusion

Zod's error handling makes it easy to validate and handle invalid data in your Node.js applications. By understanding and implementing these techniques, you can ensure your application's data integrity and provide meaningful feedback to users.


Primitive Types

Primitive Types in Node.js Zod

Primitive types are the basic building blocks of data in programming. They represent single values, such as numbers, strings, and booleans.

Basic Primitive Types

  • string: A sequence of characters enclosed in quotes.

    • Example:

      const name = zod.string();
  • number: A numeric value. Can be integers or decimals.

    • Example:

      const age = zod.number();
  • boolean: A logical value, either true or false.

    • Example:

      const isVerified = zod.boolean();

Advanced Primitive Types

  • enum: A value that can only be one of a fixed set of options.

    • Example:

      const gender = zod.enum(["male", "female", "other"]);
  • date: A representation of a calendar date.

    • Example:

      const dob = zod.date();
  • nonnegativeinteger: A non-negative integer.

    • Example:

      const id = zod.nonnegativeinteger();

Real-World Examples

User Registration Form

const userSchema = zod.object({
  name: zod.string(),
  email: zod.string().email(),
  password: zod.string().min(8),
  gender: zod.enum(["male", "female"]),
  age: zod.number(),
});

Product Database

const productSchema = zod.object({
  name: zod.string(),
  price: zod.number(),
  description: zod.string(),
  quantity: zod.nonnegativeinteger(),
});

Potential Applications

  • Data validation: Ensure that input data meets specific requirements.

  • Data serialization: Convert data to a consistent format for storage or transmission.

  • Data manipulation: Perform operations on data, such as filtering or sorting.

  • API design: Define the expected input and output data types for API endpoints.


Transformation Functions

Transformation Functions

Transformation functions allow you to modify the value of a field during validation. For example, you can convert a string to a number, trim whitespace, or convert a date to a specific format.

1. String Functions:

  • .trim(): Removes leading and trailing whitespace from a string.

  • .lowercase(): Converts a string to lowercase.

  • .uppercase(): Converts a string to uppercase.

  • .startsWith(): Checks if a string starts with a specified substring.

  • .endsWith(): Checks if a string ends with a specified substring.

  • .match(): Tests a string against a regular expression.

Example:

const schema = z.object({
  name: z.string().trim()
});

const result = schema.safeParse({ name: " John Doe " });
// result.success = true
// result.data.name = "John Doe"

2. Number Functions:

  • .toInt(): Converts a string or number to an integer.

  • .toFloat(): Converts a string or number to a floating-point number.

  • .safeParseFloat(): Similar to .toFloat(), but returns NaN for invalid inputs.

Example:

const schema = z.object({
  age: z.string().toInt()
});

const result = schema.safeParse({ age: "25" });
// result.success = true
// result.data.age = 25

3. Date Functions:

  • .date(): Converts a string or number to a Date object.

  • .preprocess() with date-fns: Allows you to easily transform dates using date-fns functions.

Example:

const schema = z.object({
  dob: z.preprocess(z.date(), dateFns.format("yyyy-MM-dd"))
});

const result = schema.safeParse({ dob: "1990-01-01" });
// result.success = true
// result.data.dob = "1990-01-01"

4. Object Functions:

  • .pick(): Selects only specified properties from an object.

  • .omit(): Removes specified properties from an object.

Example:

const schema = z.object({
  user: z.object({
    firstName: z.string(),
    lastName: z.string()
  }).pick(["firstName"])
});

const result = schema.safeParse({ user: { firstName: "John", lastName: "Doe" } });
// result.success = true
// result.data.user.firstName = "John"
// result.data.user.lastName = undefined

5. Array Functions:

  • .min() and .max(): Validate the minimum or maximum number of elements in an array.

  • .unique(): Validates that all elements in an array are unique.

Example:

const schema = z.object({
  numbers: z.array(z.number()).min(3).max(5).unique()
});

const result = schema.safeParse({ numbers: [1, 2, 3] });
// result.success = true

Real-World Applications:

  • Input Sanitization: Trim whitespace from user input to prevent unwanted spaces.

  • Data Conversion: Convert strings to numbers for calculations or dates to specific formats.

  • Data Validation: Check if a date is in a valid format or if a string matches a required syntax.

  • Object Manipulation: Extract specific properties from large objects or remove sensitive information.

  • Array Validation: Ensure that an array contains a specific number of elements or that all elements are unique.


Custom Error Messages

Custom Error Messages

Imagine you have a function that validates user input, like an email address or a password. You want to give users clear and helpful error messages if their input is invalid. Zod allows you to create custom error messages for each validation rule.

Creating Custom Error Messages

To create a custom error message, use the .transform() method when defining your validation schema. The .transform() method takes a function as its argument, and this function will receive the default error message as an argument. You can then modify the message and return a new one.

const zodSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

const customErrorMessageSchema = zodSchema.transform((error) => {
  if (error.errors[0].code === "invalid_email") {
    return { message: "Please enter a valid email address." };
  }

  return { message: error.message };
});

In this example, we've created a custom error message for the "invalid_email" error. If the input email address is invalid, the user will see the message "Please enter a valid email address." instead of the default error message.

Real-World Applications

Custom error messages are useful in various scenarios:

  • Input Validation: Provide clear and concise error messages to guide users in correcting their input.

  • API Responses: Send informative error responses to clients when API requests contain invalid data.

  • Form Validation: Display user-friendly error messages on forms to prevent submission of invalid data.

Complete Code Implementation

Here's a complete example of using custom error messages in a Node.js application:

const zod = require("zod");

const schema = zod.object({
  name: z.string().min(3),
  age: z.number().min(18),
});

const customErrorMessageSchema = schema.transform((error) => {
  if (error.errors[0].code === "too_short") {
    return { message: "The name must be at least 3 characters long." };
  } else if (error.errors[0].code === "number_too_small") {
    return { message: "The age must be at least 18." };
  }

  return { message: error.message };
});

const data = {
  name: "John",
  age: 17,
};

try {
  customErrorMessageSchema.parse(data);
} catch (error) {
  console.error(error.message); // Output: The age must be at least 18.
}

Error Handling Strategies

Error Handling Strategies in Node.js with Zod

1. Basic Validation:

  • Explanation: Checking if the input data matches the expected schema, returning errors if it doesn't.

  • Code Snippet:

const schema = z.string().min(5);
const result = schema.safeParse('Hello'); // { success: true, data: 'Hello' }

2. Customizing Error Messages:

  • Explanation: Defining specific error messages for failed validations.

  • Code Snippet:

const schema = z.string().min(5).transform((value) => value.toLowerCase());
const result = schema.safeParse('abc'); // { success: false, error: 'Value must have at least 5 characters' }

3. Nested Validation:

  • Explanation: Validating complex objects with nested schemas.

  • Code Sample:

const userSchema = z.object({ name: z.string(), age: z.number() });
const result = userSchema.safeParse({ name: 'John', age: 30 }); // { success: true, data: { name: 'John', age: 30 } }

4. Asynchronous Validation:

  • Explanation: Validating data that requires asynchronous operations (e.g., database checks).

  • Code Snippet:

const db = require('./db');

const validateUser = z.async((ctx) => await db.userExists(ctx.input.email));
const result = validateUser.safeParse({ email: 'john@example.com' }); // { success: true, data: true }

5. Middleware Validation:

  • Explanation: Attaching validation as middleware to request handlers.

  • Code Sample:

const express = require('express');
const app = express();

app.post('/users', async (req, res) => {
  const schema = z.object({ name: z.string() });
  const result = schema.safeParse(req.body);

  if (result.success) {
    // Create the user in the database
  } else {
    // Return errors to the user
  }
});

Potential Applications:

  • Ensuring data integrity in form submissions

  • Validating user input for security purposes

  • Reducing database inconsistencies by enforcing schema validations

  • Improving user experience by providing clear error messages


Intersection Types

Intersection Types

Imagine intersection types as a set of rules for combining multiple types into one, more specific type. Just like with sets in math, only values that meet all of the criteria in the intersection type can be assigned to the resulting type.

Creating Intersection Types

To create an intersection type, use the ampersand & operator:

const schema = z.object({
  name: z.string(),
  age: z.number(),
}).and(z.object({
  city: z.string(),
  country: z.string(),
}));

Example:

Let's say we want to validate a person object that must have a name, age, city, and country. We can use an intersection type:

const personSchema = z.object({
  name: z.string(),
  age: z.number(),
}).and(z.object({
  city: z.string(),
  country: z.string(),
}));

const personData = {
  name: "John Doe",
  age: 30,
  city: "New York",
  country: "USA",
};

const result = personSchema.parse(personData);

console.log(result);
// Output: { name: 'John Doe', age: 30, city: 'New York', country: 'USA' }

Applications:

Intersection types are useful in many scenarios:

  • Combining validation rules: Validate multiple aspects of an object or data structure.

  • Creating custom data types: Define complex data structures that adhere to specific criteria.

  • Enforcing type contracts: Ensure that objects match specific requirements across different parts of your codebase.


Union Types

Union Types

What are Union Types?

Union types allow you to specify that a value can be one of multiple possible types. For example, you could define a variable that can hold either a number or a string:

type NumberOrString = number | string;

Now you can assign either a number or a string to this variable:

let x: NumberOrString = 123;
x = "hello";

Why Use Union Types?

Union types are useful when you need to handle values that can have different types. For example, you might have a function that takes a number or a string as input:

function processInput(input: number | string) {
  if (typeof input === "number") {
    // Do something with the number
  } else {
    // Do something with the string
  }
}

Example: Union Type for a Database Model

Suppose you have a database table with a column that can store either a number or a string. You can define a union type to represent this column:

type ColumnValue = number | string;

// Example database model
interface User {
  id: number;
  name: ColumnValue; // Can be either a number or a string
}

Potential Applications

Union types have many applications in real-world code:

  • Handling input from forms: User input can come in different formats, such as numbers, strings, or booleans. Union types allow you to validate this input and handle it appropriately.

  • Representing data from different sources: Data from different sources may have different formats. Union types help you consolidate this data into a single type that can be easily processed.

  • Defining flexible API endpoints: You can define API endpoints that accept a variety of input types, providing flexibility to your users.


JSON Schema Generation

JSON Schema Generation with Zod

What is a JSON Schema?

A JSON Schema is a blueprint that defines the structure and format of a JSON document. It specifies allowed fields, their types, and any constraints.

Why Use Zod for JSON Schema Generation?

Zod is a powerful library that allows you to define and validate data structures in a declarative and type-safe manner. By using Zod, you can generate JSON schemas automatically from your data structures.

How to Generate a JSON Schema with Zod

import { z } from "zod";

// Define a data structure using Zod
const user = z.object({
  name: z.string(),
  age: z.number(),
  email: z.string().email(),
});

// Generate the JSON schema from the data structure
const userSchema = user.describe();

Example JSON Schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "number"
    },
    "email": {
      "type": "string",
      "format": "email"
    }
  }
}

Real-World Applications

  • API Design: JSONSchemas can define the expected input and output of API endpoints. This ensures that data is compliant with business rules and can be easily understood by developers.

  • Data Validation: JSONSchemas can be used to validate data before it is stored in a database or processed by an application. This can prevent errors and inconsistencies.

  • Documentation: JSONSchemas provide a clear and concise description of the data structure, making it easy for developers and users to understand the data format.

Potential Applications

  • Creating a REST API that accepts and returns JSON data.

  • Validating user input in a web form.

  • Generating documentation for a data model.

  • Ensuring data consistency across multiple systems.


Tuple Types

Tuple Types

What are Tuple Types?

A tuple type is a specific arrangement of different types of data. It's like a basket filled with different items, each in its own designated spot.

Creating Tuple Types:

To create a tuple type, we use square brackets [ and ]. Inside, we list the types of data that the tuple should contain.

const zooAnimals = ['Lion', 'Tiger', 'Elephant', 'Monkey'];

In this example, the zooAnimals tuple contains four different types of data: string, string, string, and string.

Accessing Tuple Elements:

We can access individual elements in a tuple by using their index. Indexes start from 0, just like in an array.

console.log(zooAnimals[0]); // Output: 'Lion'
console.log(zooAnimals[3]); // Output: 'Monkey'

Tuple Length:

The length of a tuple is the number of elements it contains. We can get the length using the length property.

console.log(zooAnimals.length); // Output: 4

Real-World Applications:

  • User profiles: A tuple can represent a user's profile, including their name, email, address, and phone number.

const userProfile = ['John Doe', 'john.doe@email.com', '123 Main Street', '123-456-7890'];
  • Product data: A tuple can represent details about a product, such as its name, price, category, and description.

const productData = ['Apple iPhone 13', 999, 'Electronics', 'The latest and greatest iPhone'];
  • Error handling: A tuple can be used to return both the error and additional context, such as the error message and affected fields.

const [error, errorMessage, affectedFields] = handleRequest();

Default Values

Default Values in Zod

Zod is a TypeScript library that helps you validate and parse data. It allows you to define schemas that specify the shape of your data, and it will automatically ensure that the data you receive matches those schemas.

One of the features of Zod is the ability to specify default values for fields. This is useful for setting a default value for a field if the user doesn't provide one.

Defining Default Values

To define a default value for a field, you use the default() method. For example:

const schema = z.object({
  name: z.string().default('John Doe'),
  age: z.number().default(20),
});

In this example, we've created a schema that defines two fields: name and age. The name field has a default value of John Doe, and the age field has a default value of 20.

Using Default Values

When you use a schema to validate and parse data, Zod will automatically set the default value for any fields that are not provided in the data. For example:

const data = {
  name: 'Jane Doe',
};

const parsedData = schema.parse(data);

console.log(parsedData);

In this example, we're parsing the data object using the schema we defined earlier. The data object only includes the name field, but Zod will automatically set the age field to its default value of 20. The output of console.log(parsedData) will be:

{
  name: 'Jane Doe',
  age: 20,
}

Real-World Applications

Default values can be useful in a variety of real-world applications, such as:

  • Setting default values for optional fields in forms.

  • Setting default values for configuration settings in applications.

  • Setting default values for database records.

Potential Applications

Here are some potential applications for using default values in Zod:

  • Forms: You can use default values to set default values for optional fields in forms. This can make it easier for users to fill out forms, as they won't have to manually enter values for every field.

  • Configuration settings: You can use default values to set default values for configuration settings in applications. This can make it easier to manage configuration settings, as you won't have to manually set every setting.

  • Database records: You can use default values to set default values for database records. This can help to ensure that all records have a valid value for every field.


Literal Types

What are Literal Types?

Literal types are a way to specify that a value must be exactly equal to a specific value. For example, you could say that a variable must be the string "hello" or the number 42.

const myString: "hello" = "hello"; // This is a literal type
const myNumber: 42 = 42; // This is a literal type

Literal types are useful for ensuring that your code is correct and does what you expect it to do. For example, if you have a function that takes a string argument, you could use a literal type to make sure that the argument is always the string "hello".

function myFunction(myString: "hello") {
  // Do something with myString
}

myFunction("hello"); // This will work
myFunction("goodbye"); // This will cause an error

How to Use Literal Types

To use literal types, you simply specify the value that the variable must be equal to. For example, to create a variable that must be the string "hello", you would write:

const myString: "hello" = "hello";

You can also use literal types with arrays and objects. For example, to create an array that must contain the numbers 1, 2, and 3, you would write:

const myArray: [1, 2, 3] = [1, 2, 3];

Real-World Applications

Literal types can be used in a variety of real-world applications. Here are a few examples:

  • Ensuring that function arguments are valid. By using literal types, you can make sure that the arguments passed to your functions are always the correct type. This can help to prevent errors and ensure that your code is robust.

  • Enforcing data consistency. By using literal types, you can make sure that your data is always consistent. For example, you could use a literal type to ensure that a database field always contains a specific value.

  • Improving code readability. By using literal types, you can make your code more readable and easier to understand. This is because it is clear what the expected value of a variable is.

Conclusion

Literal types are a powerful tool that can be used to improve the quality and robustness of your code. By using literal types, you can ensure that your code is correct and does what you expect it to do.


Custom Error Formats

Custom Error Formats

Zod is a library that validates data in Node.js. It can provide custom error messages to help developers identify and fix errors more easily.

Error Format Options

Zod provides two main error format options:

  • Default Format: Basic error message with the field name, error type, and a short description.

  • Detailed Format: More detailed error message with additional information, such as the expected type, actual value, and path to the error.

Setting the Error Format

To set the error format, use the setFormat method:

import { ZodError } from 'zod';

const schema = Zod.object({
  name: Zod.string(),
  age: Zod.number().min(18),
});

try {
  schema.parse({ name: 'John', age: 16 });
} catch (error) {
  if (error instanceof ZodError) {
    error.setFormat('detailed');
    console.log(error.message);
  }
}

Custom Error Messages

In addition to the predefined error formats, Zod allows you to define custom error messages using the invalid_type and custom errors:

  • invalid_type: Use this to provide a custom error message when a value is not of the expected type.

  • custom: Use this to provide a custom error message for any other validation failure.

Define a custom error message:

import { ZodError } from 'zod';

const schema = Zod.object({
  name: Zod.string({
    invalid_type: 'Please provide a valid name',
  }),
  age: Zod.number().min(18, {
    custom: 'You must be at least 18 years old',
  }),
});

try {
  schema.parse({ name: true, age: 16 });
} catch (error) {
  if (error instanceof ZodError) {
    console.log(error.message);
  }
}

Real-World Applications

  • Using the default error format for quick error identification.

  • Using the detailed error format for debugging and understanding complex errors.

  • Defining custom error messages to provide users with clear instructions on how to resolve errors.

Conclusion

Custom error formats in Zod allow you to customize the error messages displayed to your users. This can make it easier for them to identify and fix errors, and improve the overall user experience of your application.


Schema Inheritance

Schema Inheritance

When you have multiple schemas that share similar properties, you can use schema inheritance to reduce duplication. Instead of defining the same properties in each schema, you can define them once in a parent schema and inherit them into child schemas.

Steps to Create a Child Schema:

  • Define the base (parent) schema, which contains the shared properties.

  • Use the extend function to create a child schema that inherits the parent's properties.

  • Optionally, add additional properties to the child schema.

// Base schema
const PersonSchema = z.object({
  name: z.string(),
  age: z.number(),
});

// Child schema (inherits from PersonSchema)
const StudentSchema = PersonSchema.extend({
  grade: z.number(),
});

// Validate a student object using the child schema
const student = {
  name: "John",
  age: 20,
  grade: 12,
};

const result = StudentSchema.safeParse(student);
console.log(result.success); // true

Real-World Applications of Schema Inheritance:

  • Creating product variants (e.g., phone with different colors or storage capacities).

  • Representing different user roles (e.g., admin, customer, moderator).

  • Defining hierarchical data structures (e.g., a file system with directories and files).

  • Simplifying complex data models with shared properties (e.g., payment transactions that inherit common fields).

Advantages:

  • Reduces code duplication.

  • Enforces consistency within related schemas.

  • Enables easy updates to shared properties in the parent schema.

Limitations:

  • Child schemas inherit all validation rules from the parent schema, which may not always be desired.

  • Changes to the parent schema can break child schemas if not handled carefully.


Installation and Setup

Simplified Installation and Setup for Node.js Zod

What is Node.js Zod?

Imagine you have a website that lets users enter their age. You want to make sure they enter a valid number. Zod is like a special helper that checks if the user's input is valid.

Installing Zod:

  1. Open your terminal and type: npm install zod

  2. This will install Zod in your project.

Setting up Zod:

  1. Import Zod into your JavaScript file: import { z } from 'zod'

  2. The z object contains all the Zod helper functions.

Real-World Example:

Let's say you have the following JavaScript code:

const age = 20;

if (age < 0 || age > 150) {
  console.error("Invalid age");
} else {
  console.log("Valid age");
}

This code checks if age is between 0 and 150. If it's not, it prints "Invalid age." Otherwise, it prints "Valid age."

Using Zod to Improve the Code:

import { z } from 'zod';

const schema = z.number().min(0).max(150);

const age = schema.parse(20);

if (!schema.safeParse(age).success) {
  console.error("Invalid age");
} else {
  console.log("Valid age");
}

Benefits of Using Zod:

  • Improved error handling: Zod provides detailed error messages if the input is invalid.

  • Type safety: Zod ensures that the input is of the correct type, preventing data corruption.

  • Cleaner code: Zod simplifies validation logic, making your code more readable and maintainable.

Potential Applications of Zod:

  • Validating user input on websites and applications

  • Ensuring data integrity in databases

  • Creating API endpoints that accept only valid data

  • Developing type-safe JSON schemas


GraphQL Schema Generation

GraphQL Schema Generation with Zod

Introduction

GraphQL is a query language used for fetching data from backends. A GraphQL schema defines the data structure and operations that can be performed on that data. Zod is a library that helps you validate and shape data in Node.js. You can use Zod to automatically generate GraphQL schemas from your data validation rules.

Basic Usage

To generate a GraphQL schema from a Zod schema, you can use the z.GraphQLSchema function:

import { z, ZodType } from "zod";

const userSchema = z.object({
  id: z.string(),
  name: z.string(),
  age: z.number().optional(),
});

const schema = z.GraphQLSchema.create(userSchema);

This will generate a GraphQL schema that exposes the following fields:

type User {
  id: ID!
  name: String!
  age: Int
}

Customizing the Schema

You can customize the generated GraphQL schema by providing options to the create function. For example, you can specify the name of the root type and the description for the schema:

const schema = z.GraphQLSchema.create(userSchema, {
  rootType: "User",
  description: "A schema for representing users",
});

Field-Level Customization

You can also customize the GraphQL schema at the field level. For example, you can specify the nullability of a field or add a description:

const userSchema = z.object({
  id: z.string().nonnullable(),
  name: z.string().nonnullable(),
  age: z.number().nullable().default(0).describe("The user's age in years"),
});

const schema = z.GraphQLSchema.create(userSchema);

This will generate a GraphQL schema that exposes the following fields:

type User {
  id: ID!
  name: String!
  age: Int
}

Real-World Applications

GraphQL schema generation with Zod can be useful in various real-world applications, including:

  • Data validation: Ensure that the data sent to your backend meets the expected format and constraints.

  • API definition: Define the structure and operations of your GraphQL API in a concise and maintainable way.

  • Documentation generation: Automatically generate documentation for your GraphQL API from the Zod schemas.

Conclusion

Zod provides a convenient way to generate GraphQL schemas from data validation rules. This can help you save time and reduce errors when building GraphQL APIs.


OpenAPI Schema Generation

OpenAPI Schema Generation with Zod

OpenAPI Schema Generation helps you create OpenAPI schemas for your Node.js applications. It allows you to define your API's data structures and behavior in a standardized format that machines can understand.

Simplified Explanation

Think of OpenAPI Schema Generation like building a recipe book for your API. It tells other systems (like your front-end or documentation generator) how to understand the data that your API sends and receives.

How it Works

  1. Install Zod:

    npm install zod
  2. Define Your Schema with Zod:

    import { z } from 'zod';
    
    const userSchema = z.object({
      name: z.string(),
      age: z.number(),
      email: z.string().email(),
    });
  3. Generate the OpenAPI Schema:

    const openapiSchema = userSchema.openapi();

Code Implementation with Examples

Complete Code Implementation

import { z } from 'zod';

// Define a schema for a user
const userSchema = z.object({
  name: z.string(),
  age: z.number(),
  email: z.string().email(),
});

// Generate the OpenAPI schema
const openapiSchema = userSchema.openapi();

// Output the schema as JSON
console.log(JSON.stringify(openapiSchema, null, 2));

Example Usage

// Load the OpenAPI schema from a file
const openapiSchema = JSON.parse(fs.readFileSync('openapi-schema.json', 'utf-8'));

// Use the schema to generate documentation
const documentationGenerator = new SwaggerUI({
  spec: openapiSchema,
});

Real-World Applications

  • API Contract Enforcement: Ensure that your API's data structures and behavior match the expectations of consumers.

  • Documentation Generation: Create a comprehensive Swagger or OpenAPI documentation for your API.

  • Client Code Generation: Generate client code libraries for different programming languages based on the OpenAPI schema.

  • Testing and Validation: Use the generated schema to validate incoming API requests and responses.


Validation Options

Validation Options

Zod is a library that helps you validate and sanitize data in JavaScript. It provides a number of validation options that you can use to customize the validation process.

strip:

The strip option tells Zod to remove any extra properties from the validated data. This can be useful if you only want to validate specific properties and don't want to deal with any additional data.

const schema = z.object({
  name: z.string(),
  age: z.number(),
});

const data = {
  name: 'Alice',
  age: 20,
  extra: 'data',
};

const validatedData = schema.strip().parse(data);

console.log(validatedData); // { name: 'Alice', age: 20 }

parse:

The parse option tells Zod to try to convert the input data to the specified type. This can be useful if you want to ensure that the data is in a specific format.

const schema = z.string().email();

const data = 'alice@example.com';

const validatedData = schema.parse(data);

console.log(validatedData); // 'alice@example.com'

safeParse:

The safeParse option tells Zod to return a ZodError object if the validation fails. This can be useful if you want to handle validation errors in a specific way.

const schema = z.string().email();

const data = 'not an email';

const result = schema.safeParse(data);

if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error);
}

output:

The output option allows you to specify the output type of the validated data. This can be useful if you want to transform the data in some way before returning it.

const schema = z.string().email();

const data = 'alice@example.com';

const validatedData = schema.output(z.string().uppercase()).parse(data);

console.log(validatedData); // 'ALICE@EXAMPLE.COM'

Potential Applications:

Validation options can be used in a variety of applications, including:

  • Data validation: Ensuring that data is in the correct format and meets certain criteria.

  • Sanitization: Removing or transforming data to make it safe for use in a specific context.

  • Data transformation: Converting data from one format to another.

  • Error handling: Handling validation errors in a consistent and predictable way.


Schema Validation

What is Schema Validation?

Schema validation is like a rule book for your data. It makes sure that the data you receive or send is in the correct format and follows specific rules.

Why Use Schema Validation?

  • Ensures Data Correctness: It prevents errors and inconsistencies by checking if data meets the expected structure and format.

  • Improves Data Consistency: By defining rules, you enforce that all data follows the same standards, making it easier to work with and analyze.

  • Facilitates Communication: When sharing data between systems, schema validation ensures that everyone is working with the same understanding of the data format.

How Zod Schema Validation Works

Zod is a library in Node.js that provides schema validation. You define a schema that describes the expected data structure, and then use it to validate incoming or outgoing data.

Creating a Schema

const schema = zod.object({
  name: zod.string(),
  email: zod.string().email(),
  age: zod.number(),
});

This schema defines that an object should have a string property "name," an email address property "email," and a number property "age."

Validating Data

To validate data against a schema, you use the parse function:

const data = {
  name: "John Doe",
  email: "john.doe@example.com",
  age: 30,
};

try {
  schema.parse(data);
  console.log("Data is valid");
} catch (error) {
  console.log("Data is invalid", error.errors);
}

If the data matches the schema, the parse function returns the validated data. If there are any errors, they will be thrown as an exception.

Real-World Applications

  • API Request Validation: Validate incoming API requests to ensure they contain the correct parameters and formats.

  • Data Extraction: Parse data from various sources, such as documents or web pages, by defining schemas to extract the desired information.

  • Database Modeling: Define data models for your database to ensure that tables and columns have the correct structure.

Potential Improvements

  • Extendable Schema Definitions: Allow for custom rules and constraints to be added to schemas.

  • Improved Error Handling: Provide more detailed error messages and context to aid in debugging.

  • Schema Customization: Enable the customization of error messages and validation behavior based on specific requirements.


Recursive Types

Simplified Explanation of Recursive Types in Zod

Recursive types allow you to create schemas that reference themselves. This is useful for modeling data structures that have a nested or hierarchical structure.

Basic Example

Let's say you have a data structure that represents a tree. Each node in the tree can have children nodes, which are themselves trees. You can represent this using a recursive type:

const TreeSchema = z.object({
  value: z.string(),
  children: z.array(TreeSchema).optional(),
});

In this schema, the TreeSchema references itself as the type for the children field. This allows you to represent a tree of any depth.

Nested Validation

Recursive types allow you to perform nested validation. For example, you can use the TreeSchema to validate a nested tree of data:

const treeData = {
  value: "Root",
  children: [
    {
      value: "Child 1",
      children: [
        {
          value: "Grandchild 1",
          children: [],
        },
        {
          value: "Grandchild 2",
          children: [],
        },
      ],
    },
    {
      value: "Child 2",
      children: [],
    },
  ],
};

const result = TreeSchema.safeParse(treeData);
if (result.success) {
  // Valid tree with nested children
} else {
  // Invalid tree
}

Real-World Applications

Recursive types are useful for modeling a variety of real-world data structures, such as:

  • Hierarchical data (e.g., trees, nested JSON objects)

  • Graph data (e.g., social networks, dependency trees)

  • Recursive algorithms (e.g., Fibonacci sequence, quicksort)

Improved Examples

Example 1: Modeling an Organization Chart

const OrganizationSchema = z.object({
  name: z.string(),
  position: z.string(),
  reportsTo: OrganizationSchema.optional(),
});

This schema allows you to represent an organization chart with employees reporting to other employees.

Example 2: Validating a Mathematical Expression Tree

const ExpressionSchema = z.union([
  z.number(),
  z.object({
    operator: z.string(),
    left: ExpressionSchema,
    right: ExpressionSchema,
  }),
]);

This schema can validate a mathematical expression tree, such as one representing an arithmetic equation.

Example 3: Implementing a Recursive Binary Search Algorithm

const binarySearch = (arr, target, start = 0, end = arr.length - 1) => {
  if (start > end) return -1;
  const mid = Math.floor((end - start) / 2) + start;
  if (arr[mid] === target) return mid;
  if (arr[mid] < target) return binarySearch(arr, target, mid + 1, end);
  return binarySearch(arr, target, start, mid - 1);
};

The binarySearch function uses recursion to search for a target value in a sorted array.


Schema Composition

Schema Composition

What is Schema Composition?

Schema composition is when you take multiple smaller schemas (like recipe ingredients) and combine them to create a more complex one (like a complete recipe).

Why Use Schema Composition?

  • Keeps Validation Modular: Divide complex schemas into smaller, easier-to-manage ones.

  • Reusability: Use common schemas multiple times in different scenarios.

  • Maintainability: Easy to make changes to individual schemas without affecting the whole.

Types of Schema Composition

Object Schemas:

  • extend: Add additional fields to an existing schema.

  • partial: Extend a schema but allow optional fields.

  • merge: Combine two schemas into one with all required fields.

  • pick: Select specific fields from one or more schemas.

Array Schemas:

  • array: Create an array with nested schemas.

  • tuple: Create an array with specific field types and order.

Caveats:

  • Circular References: Avoid schemas that refer to themselves as it can lead to infinite loops.

  • Field Conflicts: When composing schemas, fields with the same name should have the same type and requiredness.

Real-World Examples:

  • Recipe Schema:

    • Ingredient schema: name, quantity

    • Recipe schema: title, ingredients (array of Ingredient)

  • User Schema:

    • BasicInfo schema: name, email

    • Address schema: street, city, zip

    • User schema: basicInfo (extend BasicInfo), address (extend Address)

  • Shopping List Schema:

    • Item schema: name, quantity

    • ShoppingList schema: items (array of Item), store

Code Implementation:

// Object composition using merge
const RecipeSchema = z.merge(
    z.object({ title: z.string() }),
    z.object({ ingredients: z.array(z.object({ name: z.string(), quantity: z.number() })) })
);

const recipe = { title: "Pizza", ingredients: [{ name: "Cheese", quantity: 2 }, { name: "Sauce", quantity: 1 }] };

const result = RecipeSchema.parse(recipe); // { title: "Pizza", ingredients: [...] }

// Array composition using array
const ItemsSchema = z.array(z.object({ name: z.string(), quantity: z.number() }));
const ShoppingListSchema = z.object({ store: z.string(), items: ItemsSchema });

const shoppingList = { store: "Walmart", items: [{ name: "Milk", quantity: 2 }, { name: "Bread", quantity: 1 }] };

const result = ShoppingListSchema.parse(shoppingList); // { store: "Walmart", items: [...] }

Applications:

  • Validating complex data structures in web forms

  • Building data models for databases or APIs

  • Defining data contracts in messaging systems

  • Enforcing data integrity in business applications


Enum Types

1. Introduction to Enum Types

In programming, an enumeration type (enum) is a set of named constants. They are typically used to represent a limited set of possible values, such as the days of the week or the colors of a traffic light.

Simplified Explanation:

  • Imagine you have a box of crayons. Each crayon has a different color name, like "red" or "blue". An enum is like a list of all the possible color names you can have in your crayon box.

  • When you use an enum, you can refer to a specific color by its name instead of writing out its value ("red" instead of "RGB(255,0,0)"). This makes your code more readable and easier to understand.

2. Creating Enum Types

In Node.js using Zod, you can create an enum type by using the enum method:

import { ZodEnum } from "zod";

const Color = ZodEnum(['red', 'blue', 'green']);

This creates an enum type named Color with three possible values: "red", "blue", and "green".

3. Using Enum Types

Once you have created an enum type, you can use it to validate data:

const schema = zod.object({
  color: Color,
});

const result = schema.parse({
  color: 'blue',
});

This schema will ensure that the color property of the input object is one of the values in the Color enum.

4. Real-World Examples

Enum types are commonly used in a variety of real-world applications, including:

  • Representing the status of an order ("placed", "shipped", "delivered")

  • Representing the priority of a task ("low", "medium", "high")

  • Representing the roles of users in a system ("admin", "user", "guest")

5. Improved Code Example

Here is an improved code example that demonstrates how to create and use an enum type to represent the status of an order:

import { ZodEnum } from "zod";

const OrderStatus = ZodEnum(['placed', 'shipped', 'delivered', 'cancelled']);

const schema = zod.object({
  status: OrderStatus,
});

const result = schema.parse({
  status: 'shipped',
});

This schema ensures that the status property of the input object is one of the valid order statuses.


Array Types

Array Types

Arrays are a collection of values that are stored in a single variable. In JavaScript, arrays can store any type of value, including other arrays.

Zod provides several ways to create and validate arrays:

  • array() - Creates an array with no specific type.

const schema = z.array();
  • array(z.string()) - Creates an array of strings.

const schema = z.array(z.string());
  • tuple(...schemas) - Creates an array with a fixed number of elements, each with a specific type.

const schema = z.tuple([z.string(), z.number(), z.boolean()]);

Validating Arrays

Zod can be used to validate arrays by checking the types of their elements and the number of elements they contain.

  • min(n) - Validates that the array has at least n elements.

const schema = z.array(z.string()).min(3);
  • max(n) - Validates that the array has at most n elements.

const schema = z.array(z.string()).max(5);
  • length(n) - Validates that the array has exactly n elements.

const schema = z.array(z.string()).length(2);

Real World Applications

Arrays are used in a wide variety of applications, including:

  • Storing data - Arrays can be used to store any type of data, including user data, product data, and financial data.

  • Representing relationships - Arrays can be used to represent relationships between objects, such as the customers of a store or the employees of a company.

  • Creating dynamic content - Arrays can be used to create dynamic content that can be easily updated and rearranged.