Supabase Edge Functions ile Otomatik E-posta Bildirimi Kurma

Sipariş gelince müdüre, stok düşünce satın almacıya otomatik bildirim gönderen bir sistem nasıl kurulur? PostgreSQL trigger + e-posta kuyruğu + Edge Function rehberi.

Sorun: Önemli Olayları Kimse Fark Etmiyor

Kurumsal bir uygulamada sürekli bir şeyler oluyor:

  • Yeni sipariş geldi → Şube müdürü bilmeli
  • Stok kritik seviyeye düştü → Satın almacı bilmeli
  • Görev atandı → Atanan kişi bilmeli

Ama kimse sürekli ekran başında oturup kontrol edemez. Otomatik bildirim lazım.

Çözüm: 3 Katmanlı Bildirim Sistemi

1. Veritabanında bir şey değişir (sipariş eklenir)

2. Trigger otomatik tetiklenir → e-posta kuyruğuna ekler

3. Edge Function kuyruktaki e-postaları gönderir

Neden doğrudan e-posta göndermiyoruz? Çünkü:

  • Hata olursa tekrar deneyebilmek istiyoruz (retry)
  • Aynı bildirimi iki kez göndermek istemiyoruz
  • Gönderim geçmişini tutmak istiyoruz

1. E-posta Kuyruk Tablosu

Gönderilecek e-postalar burada sıra bekler:

CREATE TABLE email_queue (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    recipient_email TEXT NOT NULL,
    subject TEXT NOT NULL,
    html_body TEXT NOT NULL,
    event_type TEXT NOT NULL,    -- 'siparis_olusturuldu', 'stok_kritik' vb.
    status TEXT DEFAULT 'pending', -- pending, sent, failed
    retry_count INTEGER DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    sent_at TIMESTAMPTZ,
    -- Aynı olay için aynı kişiye 1 e-posta
    UNIQUE(event_type, source_id, recipient_email)
);

2. Trigger: Olay Olunca Kuyruğa Ekle

Siparişler tablosuna yeni kayıt eklendiğinde otomatik tetiklenir:

CREATE FUNCTION bildirim_yeni_siparis()
RETURNS TRIGGER AS $$
BEGIN
    -- Şube müdürünün e-postasını bul
    -- Kuyruğa ekle
    INSERT INTO email_queue (
        recipient_email,
        subject,
        html_body,
        event_type
    ) VALUES (
        mudur_email,
        'Yeni Sipariş: ' || NEW.siparis_no,
        '<h2>Yeni sipariş oluşturuldu</h2>
         <p>Sipariş No: ' || NEW.siparis_no || '</p>
         <p>Toplam: ' || NEW.toplam || ' TL</p>',
        'siparis_olusturuldu'
    ) ON CONFLICT DO NOTHING;  -- Aynı bildirimi tekrar ekleme
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Tabloya bağla
CREATE TRIGGER trg_yeni_siparis
    AFTER INSERT ON orders
    FOR EACH ROW
    EXECUTE FUNCTION bildirim_yeni_siparis();

Artık sipariş eklenince otomatik olarak e-posta kuyruğa düşüyor.

3. Edge Function: Kuyruktakileri Gönder

Supabase Edge Function, kuyruktaki bekleyen e-postaları alıp gönderir:

// supabase/functions/send-email/index.ts
serve(async () => {
  // Bekleyen e-postaları al
  const { data: bekleyenler } = await supabase
    .from('email_queue')
    .select('*')
    .eq('status', 'pending')
    .limit(10);

  for (const email of bekleyenler) {
    try {
      // Resend API ile gönder
      await fetch('https://api.resend.com/emails', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${RESEND_API_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          from: 'Bildirim <bildirim@firma.com>',
          to: email.recipient_email,
          subject: email.subject,
          html: email.html_body,
        }),
      });

      // Başarılı → durumu güncelle
      await supabase
        .from('email_queue')
        .update({ status: 'sent', sent_at: new Date().toISOString() })
        .eq('id', email.id);

    } catch (hata) {
      // Başarısız → retry sayısını artır
      await supabase
        .from('email_queue')
        .update({ 
          retry_count: email.retry_count + 1,
          status: email.retry_count >= 2 ? 'failed' : 'pending'
        })
        .eq('id', email.id);
    }
  }
});

Otomatik Tetikleme

Edge Function’ı düzenli aralıklarla çalıştırmak için:

-- Her 5 dakikada bir çalıştır
SELECT cron.schedule(
    'eposta-gonder',
    '*/5 * * * *',  -- Her 5 dk
    $$ SELECT net.http_post(
        url := 'https://proje.supabase.co/functions/v1/send-email',
        headers := '{"Authorization": "Bearer SERVICE_KEY"}'
    ); $$
);

Sonuç: Ne Kazanırsınız?

Sipariş geldi → 5 dakika içinde müdüre e-posta
Stok düştü → Satın almacıya otomatik uyarı
Görev atandı → Kişiye bildirim

Hiçbir şey unutulmaz. Kimse sürekli ekran başında olmak zorunda kalmaz.

Bu Yaklaşım Temel Senaryolar İçin Yeter

3-5 farklı bildirim tipi için bu yapı mükemmel çalışır. Ama 15+ modül, 9 farklı rol, şube bazlı filtreler ve uygulama içi gerçek zamanlı bildirimler devreye girince mimari derinleşir.

Biz bu sistemi 30+ tablo üzerinde çalıştırıyoruz. İhtiyacınız olursa deneyimimizden faydalanabilirsiniz.


Bu yazı AstaFlow Case Study serisinin bir parçasıdır.