İçeriğe geç
Sedat Demir
Geri dön

Zod v4 Neler Getirdi? Bundle Boyutu, Performans ve Yeni API Devrimi

Zod v4 Neler Getirdi? Bundle Boyutu, Performans ve Yeni API Devrimi

TypeScript ekosisteminin en popüler schema doğrulama kütüphanesi olan Zod, uzun süredir beklenen v4 sürümünü nihayet yayınladı. Colin McDonnell'in liderliğinde geliştirilen bu büyük güncelleme, sadece küçük iyileştirmeler değil; kütüphanenin temelinden yeniden düşünüldüğü devasa bir mimari değişiklik getiriyor.

Bu yazıda Zod v4'ün neler sunduğunu, neden bu kadar heyecan verici olduğunu ve projelerinizde nasıl kullanabileceğinizi detaylıca inceliyoruz.


Zod Nedir ve Neden Bu Kadar Önemli?

Zod, TypeScript-first bir schema doğrulama kütüphanesidir. Runtime'da veri doğrulama yaparken aynı zamanda TypeScript tiplerini otomatik olarak çıkarır (infer). React Hook Form, tRPC, Next.js server actions ve daha birçok araçla entegre çalışır.

Zod v3 zaten harika bir kütüphaneydi, ancak bazı bilinen sorunları vardı:

Zod v4, tüm bu sorunları ele alan radikal bir güncelleme.


1. Bundle Boyutu: Devasa Küçülme

Zod v4'ün en çarpıcı özelliklerinden biri, bundle boyutundaki dramatik azalma. Karşılaştırmaya bakalım:

Metrik Zod v3 Zod v4 Zod Mini
Min+Gzip ~14KB ~11.5KB ~6.5KB
Azalma - ~%18 ~%54

Evet, doğru okudunuz. Zod v4 ile birlikte gelen zod/mini paketi, tam Zod deneyiminin yalnızca 6.5KB'lık bir versiyonunu sunuyor. Bu, özellikle edge runtime'lar, serverless fonksiyonlar ve mobil-first uygulamalar için oyun değiştirici bir gelişme.

Zod Mini Nasıl Kullanılır?

// Standart Zod v4
import { z } from "zod";

// Zod Mini — daha küçük bundle boyutu
import { z } from "zod/mini";

const UserSchema = z.object({
  name: z.string(),
  email: z.string().check(z.email()),
  age: z.number().check(z.minimum(18)),
});

Zod Mini, .email(), .min() gibi zincirleme yardımcı metotları kaldırarak boyutu küçültür. Bunun yerine z.check() tabanlı bir API sunar. Çekirdek doğrulama mantığı aynıdır, sadece API yüzeyi daha minimal.


2. Performans: 2x ile 7x Arası Hız Artışı

Zod v4, iç parsing motorunu sıfırdan yeniden yazdı. Sonuçlar etkileyici:

Benchmark Karşılaştırması

// 10.000 obje parse etme testi
import { z } from "zod"; // v4

const ProductSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(200),
  price: z.number().positive(),
  tags: z.array(z.string()).max(10),
  metadata: z.object({
    createdAt: z.string().datetime(),
    updatedAt: z.string().datetime(),
  }),
});

// v3: ~45ms (10K items)
// v4: ~12ms (10K items) — yaklaşık 3.7x hız artışı

Bu performans artışının ana nedenleri:


3. Yeni API Tasarımı ve Öne Çıkan Özellikler

3.1. z.interface() — Recursive Tipler İçin Nihayet Temiz Bir Çözüm

Zod v3'te recursive (özyinelemeli) tipler tanımlamak gerçek bir kabustu. z.lazy() kullanmanız ve tip anotasyonlarını manuel yazmanız gerekiyordu. Zod v4, z.interface() ile bu sorunu köklü biçimde çözüyor:

import { z } from "zod";

// Zod v4 — recursive tip tanımlama
const CategorySchema = z.interface({
  name: z.string(),
  subcategories: z.array(z.ref("CategorySchema")),
});

// Alternatif ve daha yaygın kullanım:
const TreeNodeSchema: z.ZodType = z.object({
  value: z.string(),
  children: z.lazy(() => z.array(TreeNodeSchema)),
});

// Zod v4'te z.interface() ile doğrudan:
const TreeNode = z.interface({
  value: z.string(),
  children: z.array(z.self()),
});

type TreeNode = z.infer<typeof TreeNode>;
// { value: string; children: TreeNode[] }

3.2. z.template() — Template Literal Tipleri

TypeScript'in güçlü template literal tiplerini artık Zod ile runtime'da da doğrulayabilirsiniz:

import { z } from "zod";

// Semantic versioning doğrulaması
const SemVerSchema = z.templateLiteral([
  z.number().int(),
  z.literal("."),
  z.number().int(),
  z.literal("."),
  z.number().int(),
]);

SemVerSchema.parse("1.0.0"); // ✅ Geçerli
SemVerSchema.parse("1.0");   // ❌ Hata

// CSS renk kodu doğrulaması
const HexColorSchema = z.templateLiteral([
  z.literal("#"),
  z.string().regex(/^[0-9a-fA-F]{6}$/),
]);

type HexColor = z.infer<typeof HexColorSchema>;
// `#${string}`

3.3. Yenilenen Hata Sistemi — z.prettifyError()

Zod v4, hata mesajlarını çok daha okunabilir hale getiren yeni yardımcı fonksiyonlar sunuyor:

import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().min(0).max(120),
});

const result = UserSchema.safeParse({
  name: "A",
  email: "geçersiz",
  age: -5,
});

if (!result.success) {
  console.log(z.prettifyError(result.error));
}

// Çıktı:
// ┌ ZodError
// ├ name: String must contain at least 2 character(s)
// ├ email: Invalid email
// └ age: Number must be greater than or equal to 0

3.4. z.discriminatedUnion() İyileştirmeleri

Discriminated union'lar artık çok daha performanslı ve iç içe discriminator'ları destekliyor:

import { z } from "zod";

const EventSchema = z.discriminatedUnion("type", [
  z.object({
    type: z.literal("click"),
    x: z.number(),
    y: z.number(),
  }),
  z.object({
    type: z.literal("keypress"),
    key: z.string(),
    modifiers: z.array(z.enum(["ctrl", "alt", "shift"])),
  }),
  z.object({
    type: z.literal("scroll"),
    deltaX: z.number(),
    deltaY: z.number(),
  }),
]);

// v4'te nested discriminator desteği:
const APIResponse = z.discriminatedUnion("status", [
  z.object({
    status: z.literal("success"),
    data: z.discriminatedUnion("type", [
      z.object({ type: z.literal("user"), user: z.object({ name: z.string() }) }),
      z.object({ type: z.literal("post"), post: z.object({ title: z.string() }) }),
    ]),
  }),
  z.object({
    status: z.literal("error"),
    message: z.string(),
  }),
]);

4. Yeni Metadata API'si — JSON Schema ve OpenAPI Uyumu

Zod v4, schema'lara metadata eklemeyi birinci sınıf bir özellik haline getiriyor. Bu, özellikle API dokümantasyonu ve form oluşturma araçları için kritik:

import { z } from "zod";

const ProductSchema = z.object({
  name: z.string().meta({
    title: "Ürün Adı",
    description: "Ürünün görünen adı",
    examples: ["iPhone 15", "MacBook Pro"],
  }),
  price: z.number().positive().meta({
    title: "Fiyat",
    description: "Ürün fiyatı (TL)",
  }),
}).meta({
  title: "Product",
  description: "E-ticaret ürün şeması",
  version: "1.0.0",
});

// JSON Schema'ya dönüştürme (v4 native destek)
const jsonSchema = z.toJSONSchema(ProductSchema);
console.log(JSON.stringify(jsonSchema, null, 2));

z.toJSONSchema() fonksiyonu artık Zod'un çekirdeğine dahil edildi. v3'te bunun için ayrı bir zod-to-json-schema paketi kurmanız gerekiyordu.


5. Breaking Changes ve Migration Rehberi

Zod v4, önemli breaking change'ler içeriyor. İşte dikkat etmeniz gereken başlıca değişiklikler:

5.1. .parse() Davranış Değişikliği — Strict Varsayılan

// Zod v3 — bilinmeyen alanlar sessizce silinir (strip)
const schema = z.object({ name: z.string() });
schema.parse({ name: "Ali", age: 25 }); // { name: "Ali" }

// Zod v4 — varsayılan davranış aynı kalıyor ama
// z.strictObject() artık daha yaygın öneriliyor
const strictSchema = z.strictObject({ name: z.string() });
strictSchema.parse({ name: "Ali", age: 25 }); // ❌ Hata!

5.2. .email(), .url() vb. Artık Farklı Çalışıyor

// Zod v3
z.string().email(); // zincirleme

// Zod v4 — her iki yol da çalışıyor
z.string().email();                    // hâlâ geçerli
z.email();                             // yeni kısayol!
z.string().check(z.email());           // mini uyumlu stil

5.3. z.coerce Namespace Değişikliği

// Zod v3
const schema = z.coerce.number();

// Zod v4 — aynı şekilde çalışıyor ama
// iç implementasyon tamamen değişti
const schema = z.coerce.number();
schema.parse("42");  // 42
schema.parse(true);  // 1

5.4. Error Map Değişiklikleri

// Zod v3
z.setErrorMap((issue, ctx) => {
  return { message: "Özel hata mesajı" };
});

// Zod v4 — Yeni hata yapılandırma sistemi
z.config({
  customError: (issue) => {
    if (issue.code === "invalid_type") {
      return `Beklenen: ${issue.expected}, Gelen: ${issue.received}`;
    }
  },
});

6. React Hook Form ile Zod v4 Kullanımı

Pratikte en yaygın kullanım senaryolarından biri olan React Hook Form entegrasyonu, Zod v4 ile sorunsuz çalışıyor:

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

const RegisterSchema = z.object({
  username: z.string()
    .min(3, "Kullanıcı adı en az 3 karakter olmalı")
    .max(20, "Kullanıcı adı en fazla 20 karakter olabilir")
    .regex(/^[a-zA-Z0-9_]+$/, "Sadece harf, rakam ve alt çizgi kullanılabilir"),
  email: z.email("Geçerli bir e-posta adresi giriniz"),
  password: z.string()
    .min(8, "Şifre en az 8 karakter olmalı")
    .regex(/[A-Z]/, "En az bir büyük harf içermeli")
    .regex(/[0-9]/, "En az bir rakam içermeli"),
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Şifreler eşleşmiyor",
  path: ["confirmPassword"],
});

type RegisterForm = z.infer<typeof RegisterSchema>;

function RegisterPage() {
  const { register, handleSubmit, formState: { errors } } = useForm<RegisterForm>({
    resolver: zodResolver(RegisterSchema),
  });

  const onSubmit = (data: RegisterForm) => {
    console.log("Doğrulanmış veri:", data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("username")} placeholder="Kullanıcı Adı" />
      {errors.username && <span>{errors.username.message}</span>}

      <input {...register("email")} placeholder="E-posta" />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="password" {...register("password")} placeholder="Şifre" />
      {errors.password && <span>{errors.password.message}</span>}

      <input type="password" {...register("confirmPassword")} placeholder="Şifre Tekrar" />
      {errors.confirmPassword && <span>{errors.confirmPassword.message}</span>}

      <button type="submit">Kayıt Ol</button>
    </form>
  );
}

7. Next.js Server Actions ile Zod v4

// app/actions/user.ts
"use server";

import { z } from "zod";

const CreateUserSchema = z.object({
  name: z.string().min(2),
  email: z.email(),
  role: z.enum(["admin", "user", "editor"]),
});

export async function createUser(formData: FormData) {
  const rawData = {
    name: formData.get("name"),
    email: formData.get("email"),
    role: formData.get("role"),
  };

  const result = CreateUserSchema.safeParse(rawData);

  if (!result.success) {
    return {
      success: false,
      errors: result.error.flatten().fieldErrors,
    };
  }

  // Veritabanına kaydet
  // await db.user.create({ data: result.data });

  return { success: true, data: result.data };
}

8. Zod v3'ten v4'e Geçiş Stratejisi

Büyük projelerde geçiş yaparken aşamalı bir yaklaşım öneriyorum:

  1. Zod v4'ü yükleyin: npm install zod@next (stabil sürüm çıkana kadar)
  2. Otomatik migration aracını çalıştırın: Zod ekibi bir codemod aracı sunuyor
  3. Testlerinizi çalıştırın: Mevcut test suite'iniz kırılmaları yakalayacaktır
  4. Breaking change'leri sırayla düzeltin
  5. Performans testleri yapın: Önceki ve sonraki benchmark'ları karşılaştırın
# Zod v4 kurulumu
npm install zod@^4.0.0

# Codemod ile otomatik migration
npx @zod/codemod v3-to-v4 ./src

Sonuç

Zod v4, TypeScript doğrulama ekosisteminde bir milat niteliğinde. İşte önemli çıkarımlar:

Eğer projenizde Zod v3 kullanıyorsanız, v4'e geçişi planlamaya başlamanızı şiddetle tavsiye ediyorum. Breaking change'ler olsa da, sunulan performans ve boyut kazanımları bu geçiş maliyetine fazlasıyla değiyor. Özellikle React ve Next.js projelerinde, form doğrulama ve API katmanı performansında gözle görülür iyileşmeler yaşayacaksınız.

Zod v4, "daha az boyut, daha fazla performans, daha temiz API" felsefesiyle modern web geliştirmenin ihtiyaçlarına mükemmel yanıt veriyor.


Share this post on:

Sonraki Yazı
Kysely ile Type-Safe SQL Query Builder: Tam Rehber