This content is not yet available in 🇺🇸 English. Showing the 🇹🇷 Türkçe version.
TypeScript 5.9+ ve Zod ile Tip Güvenli Full-Stack Geliştirme
Bu rehberde ne öğreneceksiniz?
Bu yazı bir haber özeti değil; adım adım uygulayabileceğiniz bir öğretici makale (tutorial) formatındadır. Her bölümün sonunda pratik çıkarımlar ve üretim ortamında karşılaşacağınız senaryolar yer alır.
- strict TypeScript yapılandırmasını bilinçli şekilde kurmak
- Zod ile runtime validation ve compile-time tipi tek kaynaktan üretmek
- Server Action ve API girişlerinde güvenli parse akışı tasarlamak
- Prisma modellerinden UI DTO'larına tip güvenli mapping yapmak
Ön koşullar
Rehberi verimli takip etmek için aşağıdaki bilgilere aşina olmanız önerilir. Eksik hissettiğiniz konularda ilgili bölümde ek kaynak ipuçları bulacaksınız.
- Modern JavaScript (ES2022+) ve async/await
- Temel React ve Next.js App Router kavramları
- HTTP ve JSON API deneyimi
Güncellik ve teknoloji yığını
Makale 2026 itibarıyla güncellenmiştir. Örnekler ve API referansları şu yığınla uyumludur: TypeScript 5.9+, Zod 3.x, Node.js 22 LTS, Next.js 16 App Router. Eski sürüm dokümantasyonu ile karıştırmamak için major versiyon farklarını özellikle belirttik.
Framework sürümleri hızla değişir; kalıcı olan prensipler (güvenlik, katman ayrımı, ölçüm) bu rehberin omurgasını oluşturur.
Bölüm 1: strict modu doğru açmak
Çoğu ekip TypeScript'i "yavaşlatıyor" sanır; asıl yavaşlatan, strict kapalıyken production'da patlayan hatalardır. tsconfig.json dosyanız projenin sözleşmesidir.
strict: true altındaki noUncheckedIndexedAccess ve exactOptionalPropertyTypes seçenekleri başlangıçta zorlayıcıdır; fakat optional alanlarda yapılan hataları erken yakalar.
Paylaşılan tipler için @/types veya domain bazlı lib/schemas klasörü kullanın; component içinde inline tip tanımı ölçeklenmez.
Adım adım uygulama
Aşağıdaki sırayı takip edin. Her adımı tamamlamadan bir sonrakine geçmeyin; özellikle güvenlik ve veri katmanı adımları atlanmamalıdır.
tsconfig.jsoniçinde"strict": trueve"skipLibCheck": trueayarlayın.- Yeni dosyalarda
anyyasaklayın; ESLint@typescript-eslint/no-explicit-anykuralını error seviyesine alın. - Mevcut legacy modülleri tek PR'da değil; modül modül strict'e taşıyın.
// tsconfig.json — önerilen çekirdek
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"moduleResolution": "bundler",
"target": "ES2017"
}
}
İpucu: CI pipeline'da tsc --noEmit çalıştırın; yalnızca IDE uyarısına güvenmeyin.
Bölüm 2: Zod ile tek kaynak prensibi
Derleme zamanı tipleri build sonrası kaybolur. API body, form ve query parametreleri runtime'da validate edilmelidir. Zod bu iki dünyayı z.infer ile birleştirir.
Şemayı bir kez yazarsınız; hem parse hem tip üretirsiniz. Hata mesajlarını .safeParse() ile kullanıcı dostu formata çevirmek form UX'inin parçasıdır.
Adım adım uygulama
Aşağıdaki sırayı takip edin. Her adımı tamamlamadan bir sonrakine geçmeyin; özellikle güvenlik ve veri katmanı adımları atlanmamalıdır.
lib/schemas/blog.tsaltında blog create şeması tanımlayın.- Server Action'da
safeParsekullanın; başarısızlıkta field-level hata dönün. - Client tarafında aynı şemayı react-hook-form resolver ile paylaşın (
@hookform/resolvers/zod).
import { z } from 'zod';
export const blogCreateSchema = z.object({
title: z.string().min(2).max(200),
content: z.string().min(17),
tags: z.string().optional(),
});
export type BlogCreateInput = z.infer<typeof blogCreateSchema>;
export function parseBlogCreate(data: unknown) {
return blogCreateSchema.safeParse(data);
}
Dikkat: Zod şemasını client bundle'a taşırken hassas business rule'ları ayırın; sadece form için gereken alanları export edin.
Bölüm 3: Prisma ile uçtan uca tip zinciri
Prisma Client 7.x otomatik üretilen tiplerle çalışır. include ve select kullanımı dönüş tipini değiştirir; UI katmanına ham Blog modelini sızdırmayın.
Prisma.BlogGetPayload<{ include: { tags: true } }> ile liste kartı tipini tanımlayın. Mapper fonksiyonları bu tipi IGetBlog gibi UI contract'ına dönüştürür.
Adım adım uygulama
Aşağıdaki sırayı takip edin. Her adımı tamamlamadan bir sonrakine geçmeyin; özellikle güvenlik ve veri katmanı adımları atlanmamalıdır.
Tip zinciri: Zod input → Service → Prisma → Mapper → React props.
- Veri erişimini
lib/data/blogs.tsiçinde toplayın. - Route handler ince kalsın: validate → service → map → response.
- Optional relation'lar için null-safe mapping yazın.
Bölüm 4: Discriminated union ile API yanıtları
Başarı ve hata yanıtlarını aynı union altında modellemek, client'ta if (result.ok) daraltmasını güvenli kılar.
type ApiResult<T> =
| { ok: true; data: T }
| { ok: false; error: string; code: string };
async function createBlog(input: unknown): Promise<ApiResult<{ id: string }>> {
const parsed = parseBlogCreate(input);
if (!parsed.success) {
return { ok: false, error: 'Validation failed', code: 'VALIDATION' };
}
const blog = await prisma.blog.create({ data: parsed.data });
return { ok: true, data: { id: blog.id } };
}
Bölüm 5: Monorepo ve paylaşılan tipler
Birden fazla paket (web, api, shared) kullanıyorsanız tiplerin tek kaynaktan gelmesi şart. packages/schemas içinde Zod + export type pattern en az bakım maliyetli yoldur.
Turborepo veya pnpm workspace ile @repo/schemas paketini hem Next.js hem worker servislerine bağlayın. Circular dependency oluşursa domain tiplerini UI bileşenlerinden ayırın.
CI'da paketler arası tsc --build veya proje referansları (composite: true) ile kırık import'ları erken yakalayın.
Adım adım uygulama
Aşağıdaki sırayı takip edin. Her adımı tamamlamadan bir sonrakine geçmeyin; özellikle güvenlik ve veri katmanı adımları atlanmamalıdır.
- Paylaşılan Zod şemalarını ayrı pakete taşıyın.
- Client-only kodu
"use client"dosyalarına izole edin; şema paketi saf TypeScript kalsın. - Versiyonlama: breaking schema değişikliğinde API ve UI'ı aynı release train'de deploy edin.
// packages/schemas/src/blog.ts
import { z } from 'zod';
export const blogIdSchema = z.string().uuid();
export type BlogId = z.infer<typeof blogIdSchema>;
Bölüm 6: Production gözlem ve tip regresyonu
Runtime'da tip hatası görülmez; fakat yanlış mapping veya serialize hataları log'da belirir.
Production ortamında TypeScript/Zod ile ilgili en sık görülen sorun, geliştirme ortamındaki varsayımların (küçük veri seti, tek kullanıcı, sıcak cache) canlı trafikte çökmemesidir. Bu yüzden her değişiklikten önce yük testi veya en azından p95 latency ölçümü yapın.
Structured logging (request id, route, süre, kullanıcı id’si — PII olmadan) ve hata oranı alarmları, sorunları kullanıcı şikayetinden önce yakalamanızı sağlar. Log’da stack trace tutun; kullanıcıya generic mesaj gösterin.
Dokümantasyonu kod ile birlikte güncelleyin: README, ADR (Architecture Decision Record) veya ekip wiki’sinde “neden bu kararı aldık?” sorusunun cevabı gelecekteki sizin en büyük yardımcınızdır.
- Sentry veya benzeri araçta
ZodErrorissues alanını tagleyin - OpenAPI diff ile contract kırılımını PR'da yakalayın
- Strict mod açıkken legacy
anysayısını metrik olarak takip edin
Sık yapılan hatalar
Aşağıdaki tuzaklar eğitim ortamlarında nadiren, production'da ise pahalıya mal olur. Code review checklist'inize eklemenizi öneririz.
- strict kapalı bırakıp yalnızca IDE'ye güvenmek
- Zod parse sonucunu kontrol etmeden Prisma'ya geçmek
- Prisma modelini doğrudan Client Component prop'u yapmak (serialize hatası)
- Generic'leri gereksiz yere karmaşıklaştırıp okunabilirliği düşürmek
Pratik alıştırmalar
Okumak yeterli değildir; öğrenmeyi pekiştirmek için küçük bir side-project veya mevcut kod tabanınızda şu görevleri uygulayın:
- Mevcut bir form için Zod şeması yazın ve hem Server Action hem client resolver'da kullanın
- Bir liste endpoint'i için
selectile dar payload tipi üretin - Hatalı input ile API'yi test edin; 422 gövdesinin alan bazlı olduğunu doğrulayın
Özet ve sonraki adımlar
Bu rehberdeki prensipleri tek seferde tüm projeye uygulamaya çalışmayın. Önce tek bir route veya modül seçin, ölçün, sonra yaygınlaştırın.
- Branded types (
type UserId = string & { __brand: "UserId" }) ile ID karışıklığını önleyin - OpenAPI veya tRPC ile contract'ı ekip dışına dokümante edin
- Prisma ORM öğreticisine geçerek veri katmanını derinleştirin