← Back to all posts
2026-08-06 · MailAnvil Team

Kirim Email Transaksional dengan MailAnvil + Supabase Edge Functions

Supabase Edge Functions jadi pilihan populer startup Indonesia: gratis 500K invocations/bulan, auto-scale, dan native PostgreSQL trigger. Tapi ada satu gap besar: nggak ada built-in email sending.

Edge Functions jalan di Deno — bukan Node.js. Nodemailer nggak bisa dipakai. SMTP connection manual ribet dan bikin cold start lambat.

Solusi: MailAnvil REST API. Satu fetch() call, nggak perlu dependency tambahan, cold start tetap di bawah 50ms.

Kenapa Bukan Nodemailer/SMTP di Edge?

Cold start Deno + Nodemailer polyfill: ~800ms
Cold start Deno + SMTP TLS handshake:  ~1200ms
Cold start Deno + MailAnvil fetch():    ~45ms

Edge Functions di-charge per GB-detik. Setiap millisecond cold start = biaya. fetch() ke MailAnvil REST API cuma satu round-trip HTTP — nggak ada TCP/TLS overhead.

Prasyarat

Step 1: Setup Supabase Edge Function

supabase functions new kirim-email

Step 2: Kode Edge Function

// supabase/functions/kirim-email/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";

const MAILANVIL_API = "https://api.mailanvil.com/v1";
const MAILANVIL_KEY = Deno.env.get("MAILANVIL_API_KEY")!;

interface EmailRequest {
  to: string;
  subject: string;
  html: string;
  from?: string;
}

serve(async (req: Request) => {
  if (req.method !== "POST") {
    return new Response("Method not allowed", { status: 405 });
  }

  const { to, subject, html, from }: EmailRequest = await req.json();

  // Validasi minimal
  if (!to || !subject || !html) {
    return new Response(
      JSON.stringify({ error: "to, subject, dan html wajib diisi" }),
      { status: 400, headers: { "Content-Type": "application/json" } }
    );
  }

  const res = await fetch(`${MAILANVIL_API}/send`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${MAILANVIL_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      from: from || "[email protected]",
      to: [to],
      subject,
      html,
    }),
  });

  const data = await res.json();

  if (!res.ok) {
    return new Response(
      JSON.stringify({ error: data.message || "Gagal kirim email" }),
      { status: res.status, headers: { "Content-Type": "application/json" } }
    );
  }

  return new Response(
    JSON.stringify({ success: true, id: data.id }),
    { headers: { "Content-Type": "application/json" } }
  );
});

Step 3: Set Environment Variable

supabase secrets set MAILANVIL_API_KEY=re_ganti_dengan_api_key_kamu

Step 4: Deploy

supabase functions deploy kirim-email

Step 5: Panggil dari Aplikasi

// Dari Next.js, React, atau Svelte client
const { data, error } = await supabase.functions.invoke("kirim-email", {
  body: {
    to: "[email protected]",
    subject: "Pesanan #1234 Dikonfirmasi",
    html: `<h1>Terima kasih!</h1><p>Pesanan kamu sudah kami proses.</p>`,
  },
});

Trigger dari Database (Opsional)

Edge Functions bisa di-trigger langsung dari PostgreSQL:

-- Trigger Edge Function setiap kali order dibuat
CREATE TRIGGER kirim_email_konfirmasi
  AFTER INSERT ON orders
  FOR EACH ROW
  EXECUTE FUNCTION supabase_functions.http_request(
    'https://[project-ref].supabase.co/functions/v1/kirim-email',
    'POST',
    '{"Content-Type":"application/json"}',
    json_build_object(
      'to', NEW.customer_email,
      'subject', 'Pesanan #' || NEW.id || ' Dikonfirmasi',
      'html', '<h1>Pesanan dikonfirmasi!</h1>'
    )
  );

Kenapa MailAnvil untuk Edge Functions?

Faktor Nodemailer + SMTP MailAnvil API
Cold start 800-1200ms ~45ms
Dependency Perlu polyfill Nol — fetch() native
DKIM/SPF Setup manual Auto-DKIM (CF DNS)
Bounce handling Manual Auto-suppression
Harga USD + kartu kredit IDR + QRIS/GoPay
Support Bahasa Inggris Bahasa Indonesia

Pola Lanjutan

Retry dengan Exponential Backoff

async function kirimDenganRetry(payload: EmailRequest, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(`${MAILANVIL_API}/send`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${MAILANVIL_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });

    if (res.ok) return await res.json();

    // Hanya retry untuk transient errors
    if (res.status >= 500) {
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
      continue;
    }

    throw new Error(`Gagal permanen: ${res.status}`);
  }
  throw new Error("Semua retry gagal");
}

Batching (hingga 50 email per call)

await fetch(`${MAILANVIL_API}/send`, {
  method: "POST",
  headers: { "Authorization": `Bearer ${MAILANVIL_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    from: "[email protected]",
    to: ["[email protected]", "[email protected]", "[email protected]"],
    subject: "Update Maintenance",
    html: "<p>Maintenance terjadwal Minggu 02:00-04:00 WIB.</p>",
  }),
});

TL;DR

Supabase Edge Functions + MailAnvil = stack email transaksional serverless untuk startup Indonesia. Nol dependency, cold start rendah, harga Rupiah, bayar QRIS.

Selengkapnya di docs.mailanvil.com →