Kami menggunakan cookies untuk meningkatkan pengalaman Anda di website ini. Dengan melanjutkan, Anda menyetujui penggunaan cookies sesuai Kebijakan Privasi kami.
Zod adalah schema validation library yang mengubah cara developer TypeScript handle input validation. Type-safe, composable, dan integrasinya mulus dengan Next.js, React Hook Form, dan tRPC.
Muhamad Putra Aulia Hidayat
Zod: Validasi Data yang Type-Safe dan Ekspresif
Kalau kamu masih validasi input secara manual dengan if-else berantai, ini saatnya beralih ke Zod. Library ini mengubah validasi dari pekerjaan membosankan jadi bagian yang menyenangkan dari development.
import { z } from "zod"
// Schema sederhana
const EmailSchema = z.string().email("Email tidak valid")
const AgeSchema = z.number().int().min(17).max(100)
// Object schema
const UserSchema = z.object({
name: z.string().min(2, "Nama minimal 2 karakter").max(50),
email: z.string().email("Email tidak valid"),
phone: z.string().regex(/^08[0-9]{8,11}$/, "Format HP tidak valid").optional(),
age: z.number().int().min(17).max(100),
role: z.enum(["admin", "user", "moderator"]).default("user"),
})
// Infer TypeScript type secara otomatis
type User = z.infer<typeof UserSchema>
// Sama dengan:
// type User = {
// name: string;
// email: string;
// phone?: string;
// age: number;
// role: "admin" | "user" | "moderator";
// }
// parse() - throw error kalau invalid
try {
const user = UserSchema.parse(formData)
// user sudah pasti valid dan type-safe
} catch (error) {
if (error instanceof z.ZodError) {
console.log(error.flatten().fieldErrors)
}
}
// safeParse() - return result object, tidak throw
const result = UserSchema.safeParse(formData)
if (!result.success) {
const errors = result.error.flatten().fieldErrors
return { errors }
}
const user = result.data // Type: User
// Array
const TagsSchema = z.array(z.string()).min(1).max(10)
// Union
const IdSchema = z.union([z.string().uuid(), z.number().int().positive()])
// Transform - ubah data saat validasi
const PriceSchema = z.string().transform(val => parseFloat(val))
// Input: "25000" → Output: 25000 (number)
// Refine - validasi custom
const PasswordSchema = z.string()
.min(8)
.refine(
(val) => /[A-Z]/.test(val) && /[0-9]/.test(val),
"Password harus mengandung huruf kapital dan angka"
)
// SuperRefine - validasi yang bergantung pada field lain
const SignupSchema = z.object({
password: z.string().min(8),
confirmPassword: z.string()
}).superRefine((data, ctx) => {
if (data.password !== data.confirmPassword) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Password tidak sama",
path: ["confirmPassword"]
})
}
})
"use client"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
const ContactSchema = z.object({
name: z.string().min(2, "Nama minimal 2 karakter"),
email: z.string().email("Email tidak valid"),
message: z.string().min(20, "Pesan minimal 20 karakter"),
})
type ContactForm = z.infer<typeof ContactSchema>
export function ContactForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting }
} = useForm<ContactForm>({
resolver: zodResolver(ContactSchema)
})
const onSubmit = async (data: ContactForm) => {
// data sudah pasti valid dan type-safe
await submitContact(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} placeholder="Nama" />
{errors.name && <p className="text-red-500">{errors.name.message}</p>}
<input {...register("email")} placeholder="Email" />
{errors.email && <p className="text-red-500">{errors.email.message}</p>}
<textarea {...register("message")} placeholder="Pesan" />
{errors.message && <p className="text-red-500">{errors.message.message}</p>}
<button type="submit" disabled={isSubmitting}>Kirim</button>
</form>
)
}
// app/api/products/route.ts
import { z } from "zod"
const CreateProductSchema = z.object({
name: z.string().min(3).max(100),
price: z.number().positive("Harga harus lebih dari 0"),
stock: z.number().int().min(0),
category_id: z.string().uuid("Category ID tidak valid"),
description: z.string().max(1000).optional(),
})
export async function POST(req: Request) {
const body = await req.json()
const result = CreateProductSchema.safeParse(body)
if (!result.success) {
return Response.json(
{
error: "Validation failed",
details: result.error.flatten().fieldErrors
},
{ status: 400 }
)
}
const product = await createProduct(result.data)
return Response.json(product, { status: 201 })
}
Zod adalah salah satu library yang setelah kamu pakai, tidak akan mau kembali ke cara lama.
Tips teknologi & bisnis mingguan
Bergabung dengan 2,500+ subscriber yang mendapatkan insight teknologi, tutorial development, dan tips bisnis digital langsung ke inbox mereka setiap minggu.
Dapatkan tips & insight teknologi terbaru langsung ke inbox Anda.
© 2026 PT Digital Uptime Teknologi Informasi. Hak cipta dilindungi.