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
- Akun MailAnvil (daftar di mailanvil.com)
- Domain terverifikasi DKIM
- Supabase project dengan Edge Functions enabled
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.