Creación de un proyecto de Nextjs

npx create-next-app nextjs-zod-validation

Creacion de una API

ve en la carpeta /app/api/register/route.js y actualiza a lo siguiente:

import { NextResponse } from "next/server";

export async function POST(request) {
  const body = await request.json();
  return NextResponse.json(body);
}

Instalación y configuración de Zod

npm i zod

Ahora crearemos un Esquema de Zod:

import { NextResponse } from "next/server";
import { z } from "zod";

const signupSchema = z
  .object({
    firstName: z
      .string({
        required_error: "First name is required",
      })
      .min(2)
      .max(50),
    lastName: z
      .string({
        required_error: "Last name is required",
      })
      .min(2)
      .max(50),
    email: z
      .string({
        required_error: "Email is required",
      })
      .email("Invalid email"),
    password: z
      .string({
        required_error: "Password is required",
      })
      .min(6)
      .max(50),
    confirmPassword: z
      .string({
        required_error: "Confirm password is required",
      })
      .min(6)
      .max(50),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: "Passwords do not match",
    path: ["confirmPassword"],
  });

export async function POST(request) {
  const body = await request.json();

  const result = signupSchema.safeParse(body);

  if (!result.success) {
    return NextResponse.json({
      errors: result.error.errors,
    });
  }

  return NextResponse.json(body);
}

Separacion de esquemas, crea una carpeta en src/schemas/auth.schema.js

import { z } from "zod";

export const signupSchema = z
  .object({
    firstName: z
      .string({
        required_error: "First name is required",
      })
      .min(2)
      .max(50),
    lastName: z
      .string({
        required_error: "Last name is required",
      })
      .min(2)
      .max(50),
    email: z
      .string({
        required_error: "Email is required",
      })
      .email("Invalid email"),
    password: z
      .string({
        required_error: "Password is required",
      })
      .min(6)
      .max(50),
    confirmPassword: z
      .string({
        required_error: "Confirm password is required",
      })
      .min(6)
      .max(50),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: "Passwords do not match",
    path: ["confirmPassword"],
  });

luego en src/api/register

import { signupSchema } from "@/app/schemas/auth.schema";
import { NextResponse } from "next/server";

export async function POST(request) {
  const body = await request.json();

  const result = signupSchema.safeParse(body);

  if (!result.success) {
    return NextResponse.json({
      errors: result.error.errors,
    });
  }

  return NextResponse.json(body);
}

si envias un request body como este pasara la validacion:

{
  "firstName": "abc",
  "lastName": "abc",
  "email": "test@test.com",
  "password": "test123",
  "confirmPassword": "test123"
}

de hecho tambien obtienen autocompletado al momento de escribir el request body:

const result = signupSchema.safeParse(body);

  if (!result.success) {
    return NextResponse.json({
      errors: result.error.errors,
    });
  }

  // you can get completion suggestions for the result object
  console.log(result.data.)