~/zafer
Tüm yazılara dön
3 dk okuma

NestJS, Prisma ve MariaDB ile ölçeklenebilir API tasarımı

NestJS, Prisma ve MariaDB ile ölçeklenebilir API tasarımı

İyi bir API, sadece endpoint listesi değildir. İstemcinin güvenebileceği bir sözleşme, veritabanının taşıyabileceği sorgular ve değişiklik karşısında dağılmayan bir kod organizasyonudur. NestJS, Prisma ve MariaDB birlikte kullanıldığında bu üç katmanı açıkça ayırmak mümkündür.

Bu yazıda bir blog ve proje yönetim sistemi üzerinden; controller, service, doğrulama, şema ve indeks kararlarını ele alıyorum.

Controller ince, service karar verici olsun

Controller'ın görevi HTTP dünyasıyla ilgilenmektir: parametreyi okumak, doğrulamayı çalıştırmak ve uygun HTTP cevabını döndürmek. Veri erişimi ve iş kuralları service katmanında kalır.

@Get()
listPublished(@Query(new ZodValidationPipe(listPostsQuerySchema)) query: ListPostsQuery) {
  return this.posts.listPublished(query);
}

Bu yapı sayesinde PostsService'i HTTP dışında da test etmek ve yeniden kullanmak kolaylaşır. Aynı servis bir yönetim endpoint'inden tüm kayıtları, herkese açık endpoint'ten ise yalnızca yayımlanmış kayıtları döndürebilir.

Doğrulamayı TypeScript'e bırakmayın

TypeScript derleme zamanında yardımcı olur; HTTP isteği ise çalışma zamanında gelir ve güvenilmezdir. İstek gövdesi için Zod şeması kullanmak, hem frontend hem API tarafında aynı sözleşmeyi taşımayı sağlar.

Bir post şeması; slug'ın küçük harf, rakam ve tireden oluşmasını; başlıkların uzunluğunu; opsiyonel alanların null olabilmesini açıkça tanımlar. API'ye geçersiz veri geldiğinde veritabanına ulaşmadan anlaşılır bir hata döner.

Paylaşılan schema paketi özellikle monorepo'da değerlidir: admin panelinin ürettiği veri ile API'nin beklediği veri ayrışmaz. Bu yaklaşımın Docker ile nasıl paketlendiğini Turborepo ve Docker ile üretime hazır bir monorepo yazısında anlattım.

Prisma şeması veri modelinin sözleşmesidir

Prisma modeli yalnızca TypeScript türü üretmez; veritabanı kısıtları ve indeks kararlarını da belgeler. Bir blog yazısında slug için @unique, kayıt kimliği için @id, zaman alanları için @default(now()) ve @updatedAt doğal tercihlerdir.

model Post {
  id        Int      @id @default(autoincrement())
  slug      String   @unique
  published Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([published, createdAt(sort: Desc)])
}

Bu indekste sorgu desenine dikkat edin. Ziyaretçi tarafındaki liste genellikle “yayınlanmış yazılar, en yeni önce” biçimindedir. [published, createdAt] bileşik indeksi bu filtreleme ve sıralama için anlamlıdır. Her alana refleksle indeks koymak doğru değildir; yazma maliyetini ve disk kullanımını artırabilir. İndeks, gerçek sorgu deseninden çıkmalıdır.

Public ve admin erişimini ayırın

GET /posts sadece yayımlanmış kayıtları döner. Taslaklar veya yönetim listesi ise token ile korunan ayrı bir route'ta bulunur. Bu ayrım sadece kullanıcı deneyimi değil, güvenlik gereksinimidir: published: false olmak erişim kontrolü değildir, sorgu kuralının da bunu uygulaması gerekir.

Yönetim route'larında Bearer token veya tercihen oturum tabanlı kimlik doğrulama kullanın. Token karşılaştırmasını sabit zamanlı yapmak, karşılaştırmanın karakter karakter erken bitmesinden doğabilecek zamanlama sinyalini azaltır. Ancak bu, token'ın güçlü ve gizli tutulması gereğini değiştirmez.

Sayfalama ve hata davranışı

Liste endpoint'i için page ve pageSize değerlerini sayıya dönüştürün, alt ve üst sınır koyun. Bu küçük önlem tek istekte yüz bin kaydın çekilmesini engeller. Cevapta items, total, page, pageSize ve totalPages vermek admin ile public arayüzün sayfalama davranışını sadeleştirir.

Bulunamayan bir slug için açıkça 404 dönün. Yetkisiz istek 401, doğrulama hatası 400 olmalıdır. Tahmin edilebilir hata sözleşmesi, istemcide özel durumları daha güvenilir yönetir.

Performansı ölçerek iyileştirin

Prisma yazarken SQL'i tamamen unutmak gerekmez. Yavaşlayan bir endpoint'te önce şu soruları sorun:

  • Sorgu gerçekten hangi alanlara göre filtreliyor ve sıralıyor?
  • Bu sorgu için uygun bir indeks var mı?
  • Sayfalama sınırlandırılmış mı?
  • Gerekmeyen büyük text veya ilişkiler seçiliyor mu?
  • Aynı istek içinde tekrar eden sorgular var mı?

Ölçümden önce yapılan “optimizasyon” çoğu zaman karmaşıklık üretir. Önce log, sorgu süresi ve veritabanı planı ile darboğazı görün; sonra şemayı veya sorguyu değiştirin.

Sonuç

NestJS modülleri sınırları korur, Zod çalışma zamanındaki veriyi doğrular, Prisma ise veri modelini ve tipleri tek yerde birleştirir. Bu üçlüyle ölçeklenebilirlik; daha fazla dosya veya daha karmaşık soyutlamalar değil, açık sözleşmeler ve gerçek sorgulara göre alınmış kararlar anlamına gelir.

NestJS, Prisma ve MariaDB ile ölçeklenebilir API tasarımı · Zafer Hüzmeli