Yirmi kişi bir CRM'de arşivlenmeliydi, ancak arayüz yeşil bir başarı rozeti gösterdi ve işlem sessizce hiçbir şey yapmadı. Supabase-JS mutasyonlarında error alanını zorunlu kılan bir TypeScript yardımcısı, artık geliştiricilerin bu hatayı halının altına süpürmek yerine onunla yüzleşmesini sağlıyor.

Neden Supabase-JS'in dönüş yapısı bir tuzaktır

Supabase'in JavaScript istemcisi (@supabase/supabase-js), bir veritabanı yazma işlemi reddedildiğinde istisna (exception) fırlatmaz. Bunun yerine, { data, error } nesnesi ile promise'i çözer (resolve eder). Eğer bir satır düzeyi güvenlik (RLS) politikası, benzersiz anahtar ihlali veya başka bir kısıtlama sorguyu engellerse, data null olarak döner ve error veritabanı mesajını içerir. İstemci, çağrıyı yapanın error alanını inceleyeceğini varsayar; çağrıyı asla iptal etmez.

Uygulamada birçok kod tabanı, bu çağrıyı "yap ve unut" (fire-and-forget) işlemi olarak ele alır:

await supabase
  .from('contacts')
  .update({ statut: 'ancien', archived_at: now })
  .eq('id', id)

return { ok: true }

update işlemi bir RLS kuralı tarafından engellendiğinde, promise yine de çözülür. Geliştirici { error } yapısını parçalamadığı (destructure etmediği) için hata görünmez kalır. Fonksiyon { ok: true } döndürür, arayüz başarı gösterir ve veriler değişmeden kalır. Hiçbir log görünmez, Sentry uyarısı tetiklenmez ve hata günlerce orada öylece durabilir.

Bir linter yeterli değildir

Statik analiz araçları, bir error özelliğinin göz ardı edildiği durumlarda uyarı verebilir ancak çalışma zamanı (runtime) sözleşmesini zorunlu kılamazlar. Bir geliştirici hala const _ = await … yazarak uyarıyı susturabilir veya kuralı bastırmak için bir yorum satırı ekleyebilir. Temel sorun, tip sisteminin error alanına hiç değinmeden bir çağrının başarılı olmasına izin vermesidir.

mutate() yardımcısı: hata yönetimini zorunlu kılmak

Yazar, dönüş tipinin yapısını değiştiren mutate() adlı küçük bir sarmalayıcı (wrapper) oluşturdu. Yardımcı, { data, error } yerine, error alanının zorunlu olduğu bir tuple [data, error] döndürür. TypeScript, ikinci elemanı göz ardı eden herhangi bir çağrının derlenmesini reddeder.

async function mutate<T>(promise: Promise<{ data: T | null; error: any }>) {
  const { data, error } = await promise
  // Send error to Sentry immediately
  if (error) Sentry.captureException(error)
  return [data, error] as const
}

Kullanım açık hale gelir:

const [result, err] = await mutate(
  supabase
    .from('contacts')
    .update({ statut: 'ancien', archived_at: now })
    .eq('id', id)
)

if (err) {
  // Handle or rethrow
  return { ok: false, message: err.message }
}
return { ok: true, data: result }

Eğer bir geliştirici err değişkenini yakalamayı unutursa, TypeScript bir hata verir: “Tuple type [T, any] of length 2 has no element at index 1.” Hata giderilene kadar kod derlenmeyecektir. Yardımcı ayrıca Sentry enstrümantasyonu ekleyerek, çağrıyı yapan kişi daha sonra hatayı görmezden gelse bile her veritabanı reddinin günlüğe kaydedilmesini garanti eder.

Neler risk altında?

  • Veri bütünlüğü – Sessiz hatalar, geçersiz durumların üretim ortamına sızmasına neden olur. Hiç aktif listeden çıkmamış bir arşivlenmiş kişi, sonraki raporların yanlış olmasına yol açabilir.
  • Kullanıcı güveni – Hiçbir şey değişmediği halde başarı bildiren bir arayüz, güveni sarsar. Müşteriler "arşivlendi" ibaresini görür ancak kişiyi aramalarda hala bulabilirler.
  • Geliştirici zamanı – Hayalet hataların peşinden koşmak saatler tüketir. Açık hata yönetimi, sorunu hata anında yüzeye çıkararak hata ayıklama döngüsünü kısaltır.
  • Operasyonel maliyet – Birkaç ekstra kod satırı ve küçük bir sarmalayıcı eklemek, sessiz bir veri kaybı vakasının maliyetiyle kıyaslandığında ihmal edilebilir düzeydedir.

Ödünleşim

Yardımcı, kodun uzunluğunu (verbosity) artırır: artık her mutasyon çağrısı bir tuple döndürür ve çağrıyı yapanlar bir if (err) bloğu yazmalıdır. Bazı ekipler, özellikle başarı bekledikleri basit CRUD işlemleri için bunu gereksiz bir kod kalabalığı (boilerplate noise) olarak görebilir. Karşı argüman ise, bu ekstra kodun isteğe bağlı bir özellik değil, bir güvenlik önlemi olduğudur. Veri doğruluğunun çok önemli olduğu ortamlarda —CRM sistemleri, finans, sağlık— bu kontrolü zorunlu kılmak kendi maliyetini fazlasıyla karşılar.

Sırada ne var?

Yazar, hiçbir açıklama yapmadan sıfır satır döndüren RLS politikalarını inceleyen üçüncü bir bölüm sözü veriyor. Bu desen, sessiz hata vakası gibi, hataları görünüşte başarılı bir sorgunun arkasına gizler. Seri, birlikte Supabase API'sinin "söylenti" tarafını ifşa etmeyi ve geliştiricilere gerçekleri talep etmeleri için somut araçlar vermeyi amaçlıyor.

Özet: Supabase-JS'in tasarımı, error alanını okumayı hatırlamadığınız sürece başarısız bir yazma işleminin başarılı gibi görünmesine izin verir. Çağrıları, error alanını zorunlu kılan ve bunu Sentry'ye kaydeden TypeScript ile desteklenmiş bir yardımcıyla sarmalayarak, sessiz hataları görünür ve aksiyon alınabilir olaylara dönüştürürsünüz. Kod boyutundaki mütevazı artış, size veri güvenilirliği ve kullanıcı güveni sağlar; ki bunlar, hiçbir sessiz başarının asla sunamayacağı iki şeydir.