Backend

Supabase ile Modern Backend Mimarisi: Hızlı ve Ölçeklenebilir

Ahmet BulutAhmet Bulut
12 Ocak 2026
12 dk

Geçen hafta bir arkadaşım Discord'dan yazdı: "Backend yazmadan ilk MVP'yi çıkarmam gerekiyor, üç gün vaktim var." Ona tek satır cevap verdim. Supabase aç, başla.

Üç gün sonra ürün canlıdaydı. Auth çalışıyordu, veritabanı düzgündü, dosya yükleme bile vardı. Ben backend'de altı yıldır kod yazıyorum ve bu hız hâlâ beni şaşırtıyor. Çünkü Supabase, yeni başlayan biri için backend'i sıkıcı olmaktan çıkarıyor; deneyimli biri için ise gereksiz tekrarı ortadan kaldırıyor.

Bu yazı tam olarak şunu yapacak: Supabase'in ne olduğunu sade dille anlatacak, ilk projenizi adım adım kurduracak ve yeni başlayanların kafayı yiyerek vazgeçtiği o gizli tuzakları önceden gösterecek. Özellikle RLS kısmını dikkatli okuyun, çünkü %90 ihtimalle ilk projenizde orada takılacaksınız.

Supabase aslında ne?

Tek cümleyle: Supabase, açık kaynaklı bir Firebase alternatifidir. Ama bu cümle yanıltıcı çünkü altta yatan teknoloji çok farklı. Firebase NoSQL bir doküman veritabanı kullanırken, Supabase tamamen PostgreSQL üzerine kurulu. Yani ilişkisel, SQL sorgulanabilir, transaction destekli, gerçek bir veritabanı.

Üstüne Supabase ekibi şu katmanları eklemiş: kullanıcı kimlik doğrulama (auth), satır bazlı güvenlik (RLS), dosya depolama (storage), gerçek zamanlı veri akışı (realtime), kenar fonksiyonları (edge functions) ve otomatik üretilen REST + GraphQL API'leri. Hepsi tek bir panelden yönetiliyor.

Neden insanlar Supabase'i seçiyor? Birkaç sebep var ve bunları sırasıyla yaşadım. Birincisi, vendor lock-in yok; Supabase'i bırakıp kendi PostgreSQL sunucunuza geçmek bir öğleden sonralık iş. İkincisi, ücretsiz katman gerçekten kullanılabilir; 500 MB veritabanı, 50 bin aktif kullanıcı, 1 GB dosya. MVP için fazlasıyla yeter. Üçüncüsü ve benim için en önemlisi, SQL'i geri getiriyor. Firebase'de query yazarken kendinizi ortaokulda hissedersiniz. Supabase'de, oturup düzgün join'lerle çalışırsınız.

İlk projenizi 5 dakikada ayağa kaldırın

Bu kısmı abartılı bir pazarlama vaadi sanmayın. Gerçekten beş dakika. Ben yazarken ölçtüm, üç dakika 40 saniye sürdü.

Önce supabase.com'a gidin ve GitHub ile giriş yapın. "New project" deyin. Bir isim verin, güçlü bir veritabanı şifresi seçin (bunu şimdi bir yere kaydedin, sonra unutursanız kurtarmak zor) ve size yakın bir bölge seçin. Türkiye'den çalışıyorsanız Frankfurt veya Londra mantıklı.

Proje hazırlanırken (yaklaşık iki dakika) terminalinizde şu komutu çalıştırın:

npm create vite@latest benim-uygulamam -- --template react-ts
cd benim-uygulamam
npm install @supabase/supabase-js

Proje hazır olduğunda Settings → API menüsünden iki şeyi kopyalayın: Project URL ve anon public key. Şimdi src/lib/supabase.ts oluşturun:

import { createClient } from '@supabase/supabase-js'

const url = import.meta.env.VITE_SUPABASE_URL const anonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(url, anonKey)

Tebrikler, backend'iniz var. Şaka değil. Bundan sonrası tablo oluşturup veri çekmek.

Asıl güç: PostgreSQL

Supabase panelinde "Table Editor" diye bir sekme var. Buradan tıklayarak tablo oluşturabilirsiniz, ama benim tavsiyem ilk günden "SQL Editor"e alışın. Çünkü tabloyu kodla oluşturursanız, daha sonra production'a taşırken aynı SQL'i tekrar çalıştırabilirsiniz. Tıklamayla yapılan şey kaybolur.

Basit bir görev tablosu kuralım:

create table tasks (
  id uuid primary key default gen_random_uuid(),
  title text not null,
  is_done boolean default false,
  user_id uuid references auth.users not null,
  created_at timestamptz default now()
);

create index tasks_user_id_idx on tasks(user_id);

Burada dikkat edilecek üç şey var. user_id kolonu doğrudan Supabase'in dahili auth.users tablosuna bağlı. Bu, kullanıcı ile veriyi ilişkilendirmenin standart yolu. Index eklemek opsiyonel görünebilir ama 10 bin satıra çıktığında olmaması canınızı sıkar. gen_random_uuid() ise integer ID yerine UUID kullanmanızı sağlıyor; özellikle birden çok client offline veri ürettiğinde işiniz kolaylaşır.

Veriyi çekmek için artık şöyle yazabilirsiniz:

const { data, error } = await supabase
  .from('tasks')
  .select('*')
  .order('created_at', { ascending: false })

İşte burada büyük ihtimalle boş bir dizi alacaksınız. Veri eklediniz, kontrol ettiniz, panel'de görünüyor. Ama kod boş döndürüyor. Hoş geldiniz, RLS dünyasına.

Auth ve RLS: en büyük tuzak burada

Row Level Security, Supabase'in en güçlü ama bir o kadar da kafa karıştıran özelliği. Açıklamak için biraz geriye gideyim.

Supabase, anon public key'i tarayıcıya gömmenizi bekler. Yani teknik olarak herhangi biri o key ile veritabanınıza istek atabilir. Bu nasıl güvenli oluyor? RLS sayesinde. PostgreSQL'in satır bazlı erişim kontrolü, her isteği kullanıcının kim olduğuna göre filtreliyor. Eğer RLS açıksa ve hiç policy yazmadıysanız, hiçbir şey görünmüyor. Yani ilk hatanız muhtemelen şuydu: tabloyu oluşturduktan sonra RLS otomatik açıldı ama bir kural yazmadığınız için hiçbir veri dönmüyor.

Şu policy'i ekleyin:

alter table tasks enable row level security;

create policy "Kullanıcılar kendi görevlerini görür" on tasks for select using (auth.uid() = user_id);

create policy "Kullanıcılar kendi görevlerini ekler" on tasks for insert with check (auth.uid() = user_id);

create policy "Kullanıcılar kendi görevlerini günceller" on tasks for update using (auth.uid() = user_id);

Şimdi auth tarafına bakalım. E-posta + şifre ile kayıt:

const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'guclu-bir-sifre-123'
})

Bir kullanıcı kayıt olduğunda Supabase otomatik bir doğrulama maili gönderir. Geliştirme aşamasında bu can sıkıcı olabilir; Authentication → Settings'ten "Confirm email" seçeneğini geçici olarak kapatabilirsiniz, ama production'da kesinlikle açın.

Magic link, Google, GitHub, Apple girişleri de aynı API'den gidiyor. Birkaç tıklamayla aktifleştirebilirsiniz. Ekibinizle birlikte aynı projede çalışıyorsanız paylaşımlı çalışma alanı üzerinden auth provider ayarlarını birlikte yönetmek mantıklı; tek bir kişinin Supabase paneline erişimi olması, ekiplerin sık yaptığı bottleneck hatalarından biri.

Storage: dosyaları neresinde tutacaksınız?

Kullanıcı profil fotoğrafı, PDF rapor, ses kaydı, ne olursa olsun Supabase Storage size S3 benzeri bir alan veriyor. Panelden bir bucket oluşturun (örnek: avatars), public mi private mı olacağına karar verin.

Yükleme şu kadar basit:

const file = e.target.files[0]
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(${userId}/avatar.png, file, {
    upsert: true
  })

Storage'ın da kendi RLS policy'leri var. Yeni başlayanlar genelde dosya yüklemeyi başarır ama indirmek isteyince "unauthorized" yer. Çünkü bucket private ve okuma policy'si yazılmamış. Aynı tablolardaki gibi, storage.objects üzerinde de policy yazmanız gerekiyor. Bu mantığı ilk kez kavradığınızda Supabase'in tutarlılığına hayran kalırsınız: her şey aynı güvenlik modeline yaslanmış.

Realtime ve Edge Functions: sırada bunlar var

Realtime, PostgreSQL'in WAL (Write-Ahead Log) akışını dinleyip değişiklikleri WebSocket üzerinden client'a iletiyor. Yani başka bir kullanıcı bir görev eklediğinde, sayfayı yenilemeden anında ekrana düşmesini istiyorsanız üç satır kod yetiyor:

supabase
  .channel('tasks-changes')
  .on('postgres_changes', 
    { event: '*', schema: 'public', table: 'tasks' },
    payload => console.log('Değişiklik:', payload)
  )
  .subscribe()

Edge Functions ise Deno üzerinde çalışan sunucusuz fonksiyonlar. Stripe webhook'u almak, OpenAI API'sine istek atıp anon key'i gizlemek, e-posta tetiklemek gibi sunucu tarafında olması gereken işler için ideal. CLI ile lokal'de yazıp supabase functions deploy ile yayınlıyorsunuz. Production'a geçtiğinizde bu olmazsa olmaz; her şeyi client'tan yapamazsınız.

Yeni başlayanların düştüğü gerçek hatalar

Şimdi sıkı durun, çünkü bu kısım size gerçekten zaman kazandıracak. Üç ana hata var ve her biri en az bir gecemi yedi.

Birinci hata, RLS açıkken policy yazmamak. "Veri gelmiyor!" diye saatlerce kod debug edersiniz. Çözüm: tabloyu kurar kurmaz select, insert, update, delete için policy'leri yazın. Tembellik yapıp "sonra eklerim" demeyin, sonra unutursunuz.

İkinci hata, anon key ile service_role key'i karıştırmak. Service role tüm RLS'i bypass eder. Eğer yanlışlıkla bunu frontend'e koyarsanız, veritabanınız tamamen açık olur. Service role'ü sadece sunucu tarafında ya da Edge Function içinde kullanın. Erişim raporları tutmak isterseniz ekibinizin hangi key'i nerede kullandığını şeffaf takip etmek hayat kurtarır.

Üçüncü hata, foreign key kullanmadan tablolar oluşturmak. Sonra join atmaya çalıştığınızda performans dibe vurur ve veri tutarsız olur. PostgreSQL ilişkisel bir veritabanı, ilişkileri kullanın. references ifadesi sizin dostunuz.

Bir bonus dördüncü hata da migration'ları takip etmemek. Panel'den el ile tablo oluşturup, başka bir ortamda aynı yapıyı kuramayınca panik yaşamak çok yaygın. Supabase CLI'ı kurun, supabase db diff komutuyla migration dosyaları üretin, bunları git'e koyun. Üç ay sonra kendinize teşekkür edeceksiniz.

Ekiple çalışırken ne değişir?

Tek başına çalışırken Supabase pürüzsüz akar. Ama ekibe iki kişi daha eklendiği anda iş karışmaya başlar. Kim hangi tabloyu değiştirdi? Hangi policy bozuldu? Migration sıralaması ne? Bu süreçte bir proje yönetim aracı kullanmak, kod tarafındaki kaosu azaltıyor; her schema değişikliğini bir göreve bağlamak, code review kültürünü oturtuyor.

Poitim'i tam olarak bunun için tasarladık aslında. Backend ekibi Supabase migration'ı yazarken, frontend ekibi aynı task altında client tarafındaki değişikliği bekliyor. Sprint takvimi üzerinden deploy günlerini görebilmek de iletişimi rahatlatıyor. Eğer kendi ekibinizde böyle bir akış kurmak istiyorsanız demo üzerinden bakabilirsiniz.

Supabase'i öğrenmenin en iyi yolu, küçük bir proje seçip ilk gün yayına almak. Mükemmelleştirmeye çalışmayın. Bir not alma uygulaması, bir okuma listesi, bir mini blog. Yayına aldıktan sonra kullanıcı geri bildirimleriyle iyileştirin. Backend'in görünmez olduğu, sadece ürüne odaklandığınız bu aralık çok değerli. Onu hak edin.

Sık Sorulan Sorular

Supabase Nedir? Modern Backend Mimarisi Rehberi