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

ArkType ile Runtime Type Validation: TypeScript Syntax'ını Runtime'a Taşıyın

ArkType ile Runtime Type Validation: TypeScript Syntax'ını Runtime'a Taşıyın

TypeScript geliştiricileri olarak hepimiz aynı sorunu yaşıyoruz: compile-time'da mükemmel tip güvenliğine sahipken, runtime'da API'den gelen veriler, kullanıcı girdileri veya harici kaynaklardan okunan datalar karşısında tamamen savunmasız kalıyoruz. Bu boşluğu doldurmak için Zod, Yup, io-ts gibi kütüphaneler ortaya çıktı. Ancak bu kütüphanelerin hepsinde ortak bir sorun var: kendi özel syntax'larını öğrenmeniz gerekiyor.

Peki ya runtime validation şemalarınızı doğrudan TypeScript syntax'ı ile yazabilseydiniz? İşte tam olarak ArkType'ın sunduğu devrimsel yaklaşım bu.

ArkType Nedir?

ArkType, TypeScript'in tip syntax'ını runtime'da çalıştırabilen bir validation kütüphanesidir. 2024 yılında 2.0 sürümüyle olgunlaşan bu araç, string tabanlı type expression'lar kullanarak TypeScript yazıyormuş hissini runtime validation'a taşır.

ArkType'ın temel felsefesi şudur: Zaten TypeScript biliyorsanız, yeni bir DSL öğrenmenize gerek yok.

ArkType'ın Öne Çıkan Özellikleri

Kurulum ve İlk Adımlar

ArkType'ı projenize eklemek son derece basit:

npm install arktype

İlk type tanımımızı oluşturalım:

import { type } from "arktype";

// TypeScript syntax'ına ne kadar benzediğine dikkat edin
const user = type({
  name: "string",
  email: "string.email",
  age: "number",
});

// Type inference otomatik olarak çalışır
type User = typeof user.infer;
// { name: string; email: string; age: number }

// Validation
const result = user({
  name: "Ahmet",
  email: "ahmet@example.com",
  age: 28,
});

if (result instanceof type.errors) {
  console.error(result.summary);
} else {
  console.log(result); // Tip güvenli data
}

Gördüğünüz gibi, "string", "number" gibi ifadeler doğrudan TypeScript'in primitive tiplerini yansıtıyor. Zod'daki z.string(), z.number() gibi method chain'lere kıyasla çok daha doğal bir yazım sunuyor.

Temel Tip Tanımları

Primitive Tipler

import { type } from "arktype";

const primitives = type({
  name: "string",
  age: "number",
  isActive: "boolean",
  createdAt: "Date",
  id: "string | number",      // Union type - tıpkı TypeScript gibi!
  metadata: "unknown",
  callback: "Function",
});

String Validation'ları

ArkType, string tipine özel validation'lar için güçlü alt-tipler sunar:

const contactForm = type({
  email: "string.email",
  website: "string.url",
  phone: "string.regex",
  uuid: "string.uuid",
  ip: "string.ip",
});

Sayısal Kısıtlamalar

TypeScript'e benzer ama runtime'da çalışan sayısal constraint'ler:

const product = type({
  name: "string > 0",              // En az 1 karakter
  price: "number > 0",             // Pozitif sayı
  quantity: "number.integer >= 0", // Negatif olmayan tam sayı
  discount: "0 <= number <= 100",  // 0-100 arası
  rating: "1 <= number <= 5",      // 1-5 arası rating
});

const result = product({
  name: "Laptop",
  price: 15999.99,
  quantity: 50,
  discount: 15,
  rating: 4.5,
});

Bu sözdizimi gerçekten etkileyici — "0 <= number <= 100" ifadesi matematiksel notasyona ne kadar yakın değil mi?

İleri Düzey Kullanım

Array ve Tuple Tipleri

const config = type({
  tags: "string[]",                    // String dizisi
  scores: "number[]",                 // Sayı dizisi
  matrix: "number[][]",               // İç içe dizi
  pair: ["string", "number"],         // Tuple: [string, number]
  coordinates: ["number", "number", "number"], // 3D koordinat
});

Optional ve Default Değerler

const userProfile = type({
  name: "string",
  "email?": "string.email",          // Optional field
  age: "number = 18",                // Default değer
  "bio?": "string",
  role: "'user' | 'admin' = 'user'", // Literal union + default
});

const result = userProfile({
  name: "Zeynep",
});
// { name: "Zeynep", age: 18, role: "user" }

Nested (İç İçe) Objeler

const blogPost = type({
  title: "string > 0",
  content: "string",
  author: {
    name: "string",
    email: "string.email",
    "avatar?": "string.url",
  },
  tags: "string[]",
  metadata: {
    publishedAt: "Date",
    "updatedAt?": "Date",
    views: "number.integer >= 0",
  },
});

type BlogPost = typeof blogPost.infer;

Union ve Intersection Tipleri

// Discriminated union - TypeScript'teki gibi
const shape = type({
  kind: "'circle'",
  radius: "number > 0",
}).or({
  kind: "'rectangle'",
  width: "number > 0",
  height: "number > 0",
});

// Intersection ile tip birleştirme
const timestamped = type({
  createdAt: "Date",
  updatedAt: "Date",
});

const baseEntity = type({
  id: "string.uuid",
  name: "string",
});

const entity = baseEntity.and(timestamped);
type Entity = typeof entity.infer;
// { id: string; name: string; createdAt: Date; updatedAt: Date }

Zod ile Karşılaştırma

ArkType'ın değerini tam olarak anlamak için, aynı şemayı hem Zod hem de ArkType ile yazalım:

Zod ile:

import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  role: z.enum(["user", "admin"]).default("user"),
  address: z.object({
    street: z.string(),
    city: z.string(),
    zipCode: z.string().regex(/^\d{5}$/),
  }).optional(),
  hobbies: z.array(z.string()).min(1),
});

type User = z.infer<typeof UserSchema>;

ArkType ile:

import { type } from "arktype";

const User = type({
  name: "string > 0",
  email: "string.email",
  age: "0 <= number.integer <= 150",
  role: "'user' | 'admin' = 'user'",
  "address?": {
    street: "string",
    city: "string",
    zipCode: "string.regex",
  },
  hobbies: "string[] > 0",
});

type User = typeof User.infer;

Fark açıkça görülüyor: ArkType versiyonu daha kısa, daha okunabilir ve TypeScript'e çok daha yakın.

Performans Karşılaştırması

ArkType'ın en büyük avantajlarından biri performans. Benchmark sonuçlarına göre:

Kütüphane Saniyedeki İşlem (ops/sec) Bundle Size
ArkType ~10,000,000 ~30KB
Zod ~100,000 ~57KB
Yup ~50,000 ~75KB

ArkType, tip tanımlarını derleme aşamasında optimize ettiği için runtime'da çok daha hızlı çalışır. Bu, özellikle yüksek throughput gerektiren API endpoint'lerinde kritik bir avantajdır.

Gerçek Dünya Örneği: Express API Validation

ArkType'ı bir Express.js API'sinde nasıl kullanacağınızı görelim:

import express from "express";
import { type } from "arktype";

const app = express();
app.use(express.json());

// Request body şeması
const CreateOrderBody = type({
  customerId: "string.uuid",
  items: [{
    productId: "string.uuid",
    quantity: "number.integer > 0",
    "note?": "string",
  }, "[]"], // Array of objects
  "couponCode?": "string",
  shippingAddress: {
    line1: "string > 0",
    "line2?": "string",
    city: "string > 0",
    state: "string > 0",
    zipCode: "string",
    country: "string == 2", // Tam 2 karakter (ülke kodu)
  },
});

// Generic validation middleware
function validate<T>(schema: type.Any) {
  return (req: express.Request, res: express.Response, next: express.NextFunction) => {
    const result = schema(req.body);
    if (result instanceof type.errors) {
      return res.status(400).json({
        error: "Validation failed",
        details: result.summary,
      });
    }
    req.body = result; // Validated ve parsed data
    next();
  };
}

app.post("/api/orders", validate(CreateOrderBody), (req, res) => {
  // req.body artık tip güvenli
  const order = req.body;
  console.log(order.customerId); // string - tip güvenli!
  res.json({ success: true, order });
});

Morph (Dönüşüm) Özelliği

ArkType sadece validation yapmaz, veriyi dönüştürme (transform) yeteneğine de sahiptir:

import { type } from "arktype";

// String'den number'a dönüşüm
const numericString = type("string.numeric.parse");

const result = numericString("42");
// result: 42 (number tipinde)

// Tarih parse etme
const dateString = type("string.date.parse");

const date = dateString("2024-01-15");
// date: Date objesi

// Özel dönüşümler ile
const percentage = type("string")
  .pipe((s) => parseFloat(s))
  .pipe(type("0 <= number <= 100"));

const val = percentage("85.5");
// val: 85.5 (validated number)

Hata Yönetimi

ArkType'ın hata mesajları son derece okunabilirdir:

const user = type({
  name: "string > 0",
  email: "string.email",
  age: "0 <= number.integer <= 150",
});

const result = user({
  name: "",
  email: "invalid-email",
  age: -5,
});

if (result instanceof type.errors) {
  console.log(result.summary);
  // name must be more than 0 characters (was 0)
  // email must be an email address (was "invalid-email")
  // age must be at least 0 (was -5)
}

ArkType Ne Zaman Tercih Edilmeli?

ArkType'ı seçmeniz gereken durumlar:

  1. Performans kritik uygulamalar: Yüksek throughput gerektiren API'ler
  2. TypeScript ekipleri: Ekip zaten TypeScript'e hakimse, öğrenme eğrisi minimum olur
  3. Okunabilirlik öncelikli projeler: Şema tanımlarının kolayca anlaşılması gerektiğinde
  4. Bundle size hassasiyeti: Frontend uygulamalarında her KB önemli olduğunda

Zod'u tercih etmeniz gereken durumlar:

  1. Olgun ekosistem gereksinimi: tRPC, React Hook Form gibi entegrasyonlar
  2. Geniş topluluk desteği: Daha fazla Stack Overflow cevabı, blog yazısı
  3. Karmaşık custom validation'lar: .refine() ve .superRefine() ile esnek kurallar

Sonuç

ArkType, TypeScript dünyasında runtime validation yaklaşımını yeniden tanımlayan cesur bir kütüphane. "TypeScript biliyorsan, ArkType'ı da biliyorsun" mottosu gerçekten karşılığını buluyor. String-based type expression'lar sayesinde şema tanımları hem kısa hem de son derece okunabilir oluyor.

Performans konusundaki üstünlüğü, TypeScript-native syntax'ı ve küçük bundle size'ı ile ArkType, özellikle 2024-2025 döneminde Zod'a ciddi bir alternatif olarak yükseliyor. Eğer yeni bir proje başlıyorsanız veya mevcut validation katmanınızı modernize etmek istiyorsanız, ArkType'a mutlaka bir şans verin.

Unutmayın: En iyi validation kütüphanesi, ekibinizin en verimli kullandığı kütüphanedir. ArkType'ın TypeScript'e yakın doğası, birçok ekip için bu verimi doğal olarak artıracaktır.


Share this post on:

Sonraki Yazı
Expo Router v4 ile Web ve Mobile İçin Tek Codebase Geliştirme Rehberi