Logo
Logo

Blog

Zod in React and Next Js

Stay informed on new product features, the latest in technology, solutions, and updates.

Sep 2, 2023

presentation

Introduction

When developing applications, integrating external data is a common requirement, whether obtained from backend servers, user inputs, local storage, or URLs. Despite TypeScript's robust static typing capabilities, validating this incoming data is crucial to ensure its integrity and conformity to expected schemas.

While TypeScript excels at providing static type checking, Zod serves as a valuable complement by offering runtime validation and complex schema definition capabilities. This dynamic validation ensures data consistency and correctness beyond compile-time checks, making it indispensable in scenarios involving external data sources like APIs or user input. Additionally, Zod schemas act as comprehensive documentation for data structures, facilitating better understanding and maintenance of the application.

With features such as data transformation and server-side validation, Zod extends TypeScript's capabilities, enhancing overall data reliability and security in TypeScript-based projects.

1. Form Validation

Zod can be used for validating form inputs in React components. You can define schemas for the expected shape of form data and then use Zod's validation functions to ensure that the data entered by users conforms to those schemas before submitting the form.

In the real world, tools like React Hook Form or Formik prove invaluable for managing form validation, error handling, loading states, and optimizing performance by preventing unnecessary re-renders. While React Hook Form facilitates validation on the client side, it's essential to consider the validation needs on the backend, where incoming data must be checked thoroughly. This is where Zod emerges as a crucial asset.

When data is submitted to the backend, it's similar to handling incoming data, requiring validation. By employing Zod, which shares schemas between frontend and backend, developers can ensure consistent validation across both ends of the application. Unlike React Hook Form, which primarily operates on the client side, Zod's schemas can be reused for validation on both client and server sides. Thus, while React Hook Form streamlines client-side form validation, Zod extends this capability to the backend, promoting code reuse, consistency, and reliability throughout the application stack.

Here's an example of how Zod can be used for form validation in a React component:

import { z } from "zod";

const productSchema = z.object({
id: z.number().optional(),
name: z.string().min(1),
price: z.number().min(10),
});
export default productSchema;

Client side

import React from "react";
import { useForm } from "react-hook-form";
import productSchema from "@/lib/validation";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

type Product = z.infer<typeof productSchema>;
const AddProduct = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Product>({
resolver: zodResolver(productSchema),
});
const onSubmit = (data: Product) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register("name")} />
<input {...register("name", { required: true })} />
{errors.name && <p>Product name is required.</p>}
<input {...register("price")} />
{errors.price && <p>Please enter number for age.</p>}
<input type="submit" />
</form>
);
};

export default AddProduct;

Server side ,controller for adding new Product

import { request } from "http";
import { NextRequest, NextResponse } from "next/server";
import productSchema from "@/lib/validation";

export const POST = async (request: NextRequest) => {
const body = await request.json();
const validateProduct = productSchema.safeParse(body);
// validateProudct using zod
if (!validateProduct.success) {
return NextResponse.json({ error: validateProduct.error, status: 422 });
}
// add product to the data base
};

2. API Response Validation

When working with APIs in React or Next.js applications, it's essential to validate the response data from the server to ensure it matches the expected format. Zod can be used to define schemas for API response data and validate it before using it in components or passing it down to child components.

Client Side Without Zod Validation

'use client'
import { useEffect } from "react";

interface Product{
name:string,
price:number
}
const Product=()=>{
useEffect(()=>{
fetch('api/products')
.then((response)=>{
return response.json();
}).then((product:Product)=>{
console.log(product.name.toUpperCase);
// it may crash the application if there is no name field in the product
// console.log(product?.name?.toUpperCase);
// we can solve this problem using the optional chaining but it becomes messy as code large data base
})

},[])
return (
<div>Product</div>
)
}

export default Product;

Server Side

import { NextRequest, NextResponse } from "next/server";

export const GET = async (request: NextRequest) => {
const product = {
name: "Apple",
price: 100,
};

return NextResponse.json({ product });
};

We can the make the code robust using zod validations ,it also provides the error message

'use client'
import { useEffect } from "react";
import {z} from "zod";

interface Product{
name:string,
price:number
}
const productSchema=z.object({
name:z.string(),
price:z.number()
})
const Product=()=>{
useEffect(()=>{
fetch('api/products')
.then((response)=>{
return response.json();
}).then((product:Product)=>{
const validateProduct=productSchema.safeParse(product);
if(!validateProduct.success)
{
console.log(validateProduct.error);
return ;
}
// console.log(validateProduct.data.name.toFixed());
// Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?

console.log(validateProduct.data.name);
console.log(validateProduct.data.price);
})

},[])
return (
<div>Product</div>
)
}

export default Product;

3. Third-Party API Response Validation

Sometimes, we need to utilize third-party APIs in our applications to fetch data that is useful for our needs. We can handle responses from these APIs similarly to how the API response was defined above.

4. Local Storage

Zod can validate data retrieved from localStorage in a React application. By defining a schema and using Zod's safeParse function, it ensures the integrity of the stored data, handling validation errors if necessary. This approach ensures the reliability of application data, crucial for maintaining consistency and preventing issues arising from invalid or unexpected data.

Here's an example of how Zod can be used to validate data retrieved from localStorage:

"use client";
import { z } from "zod";

const cartSchema = z.array(
z.object({
id: z.number(),
quantity: z.number(),
})
);
type Cart = z.infer<typeof cartSchema>;
const Cart = () => {
const cart = JSON.parse(localStorage.getItem("cart") || "[]");
const validateCart = cartSchema.safeParse(cart);
if (!validateCart.success) {
localStorage.removeItem("cart");
return;
}
// validate through the zod
validateCart.data.map((cart) => {
console.log(cart.id);
console.log(cart.quantity);
});

return <div>cart</div>;
};
export default Cart;

5. Environment Variable Validation

Using Zod to validate environment variables ensures configuration consistency, catches errors early, improves security, provides clear documentation, and simplifies maintenance.

Here's an example of how environment variables can be validated using Zod:

import z from "zod";

const environmentSchema = z.object({
MONGO_URL: z.string().min(1),
JWT_KEY: z.string().min(1),
});
export const validateEnvironmentSchema = environmentSchema.parse(process.env);

Here is an example

import { validateEnvironmentSchema } from "@/env";
import mongoose from "mongoose";

const connectDatabase = async () => {
try {
mongoose.connect(validateEnvironmentSchema.MONGO_URL);
// Mongo url from the validateEnvironmentSchema
const connection = mongoose.connection;
connection.on("connected", () => {
console.log("database connected succesfully");
});
connection.on("error", (err) => {
console.log("someting went wrong");
console.log(err);
});
} catch (err) {
console.log(err);
}
};
export default connectDatabase;

6. File System

Zod provides concise schema definitions and robust validation capabilities when handling data from the file system. By defining clear schemas and validating data against them, Zod ensures data integrity, reduces errors, and facilitates smooth data handling processes.

Here's an example of how Zod can be used to validate data from the file system:

Product Schema

import { z } from "zod";

const productSchema = z.object({
id: z.number().optional(),
name: z.string().min(1),
price: z.number(),
});
export default productSchema;
import { promises as fs } from "fs";
import productSchema from "@/lib/validation";
import { NextRequest, NextResponse } from "next/server";

export const GET = async (request: NextRequest) => {
const jsonData = await fs.readFile(
process.cwd() + "/src/data/product.json",
"utf8"
);
const product = JSON.parse(jsonData);

const validateProduct = productSchema.safeParse(product);
if (!validateProduct.success) {
return NextResponse.json({ error: validateProduct.error, status: 500 });
}

return NextResponse.json({ products: validateProduct.data });
};

Conclusion

Integrating Zod with React and Next.js applications provides a robust solution for ensuring data integrity, consistency, and security. While TypeScript offers static type checking, Zod extends this by providing runtime validation, complex schema definitions, and dynamic validation capabilities. This combination enhances the development process by offering comprehensive documentation, data transformation utilities, and server-side validation, thereby improving overall reliability and maintainability of applications.

By leveraging Zod alongside TypeScript in React and Next.js projects, developers can achieve greater confidence in their data handling processes, leading to more robust and resilient applications.

Logo

Crafting Quality Software in India, Shaping Global Excellence

Flag of India

Proudly building in India

A-401, Pramukh Anand Orbit Mall,
Kudasan, Gandhinagar, Gujarat 382421

© 2024 BigCircle. All rights reserved

This website uses cookies to ensure you get the best experience on our website. By continuing to use this site, you agree to our Cookie Policy