Zod is one of the best things that happened in the JavaScript community and became indispensable for me alongside tools like lodash, vite, pnpm and prettier.
Using zod Functions you can easily isolate your business logic and reuse in different contexts like a classic Express app, a Serverless function in AWS Lambda or Netlify Functions, a Next.js route, Server Action or Remix API route and map the errors to return an appropriate response on each framework.
The schemas are easy to understand, create and edit. The .coerce easily transforms your params like path or query params. You can add async validations using .refine and then .parseAsync, document fields and schemas using .describe and even edit the input before validating with .pipe.
Way simpler and more powerful than the verbose Nest.js Pipes and class-validator for example, creating custom Schema directives in your GraphQL type definitions, or verbose JSON Schemas with ajv.
import { z } from "zod"
const numberParam = z.coerce.number().int().min(0).default(0)
const addNumbers = z
.function()
.args(
z.object({
a: numberParam,
b: numberParam,
})
)
.implement(({ a, b }) => {
return a + b
})
const input: any = { a: "1", b: "2" }
console.log(addNumbers(input))
// 3If you are using a monorepo structure like Nx or Turbo you can share the input schemas and typings when needed between different implementations or your server and client using libs or use a private NPM repository.
If you are using react-hook-form, the Zod resolver it will be useful.
With the proper JSDoc comments, if you are using VS Code you might not even need TypeScript and rely heavily on zod inferred types.