Panduan Silverhawk WebLearn · Backend gratis

Koneksi GitHub Pages
ke Supabase

Panduan lengkap dari belum punya akun Supabase sampai komentar komunitas tersimpan bersama (bukan hanya localStorage). Cocok untuk website statis yang di-deploy di GitHub Pages.

Waktu setup~20–40 menit
BiayaGratis (Free tier)
SkillPemula → Menengah
HasilKomentar real-time

Kenapa perlu backend seperti Supabase?

Silverhawk WebLearn saat ini menyimpan komentar di localStorage browser pengunjung. Artinya:

Supabase adalah Backend-as-a-Service (BaaS) open-source berbasis PostgreSQL. Free tier-nya cukup longgar untuk website belajar/komunitas kecil:

Free tier Supabase (per Maret 2025+): 500 MB database, 1 GB file storage, 2 juta edge function invocations, realtime connections terbatas tapi cukup untuk demo/komunitas kecil. Tidak butuh kartu kredit untuk mulai.

1. Buat akun Supabase (dari nol)

Buka situs Supabase

Kunjungi https://supabase.com → klik Start your project / Sign up.

Daftar

Pilih cara daftar:

  • GitHub (paling praktis, karena kamu sudah pakai GitHub Pages)
  • atau email + password

Ikuti verifikasi email jika diminta.

Masuk ke Dashboard

Setelah login kamu akan masuk ke Dashboard. Jika belum punya organization, buat saja (nama bebas, misalnya “Silverhawk”).

2. Buat Project baru

New Project

Klik New project. Isi:

  • Name: silverhawk-weblearn (atau apa saja)
  • Database Password: buat password kuat, simpan di tempat aman (kamu butuh ini nanti jika connect via SQL client)
  • Region: pilih yang dekat (Singapore / Tokyo biasanya baik untuk Indonesia)
  • Pricing Plan: Free

Klik Create new project. Tunggu 1–2 menit sampai status Active.

Jangan share Database Password ke publik. Yang akan kamu pakai di frontend hanya anon / public key.

3. Buat tabel komentar

Di sidebar kiri, buka Table Editor → New table.

Atau lebih cepat lewat SQL Editor → New query, lalu jalankan:

-- Tabel komentar komunitas
create table public.comments (
  id uuid primary key default gen_random_uuid(),
  name text not null,
  topic text,
  message text not null,
  avatar_seed text default '1',
  created_at timestamptz not null default now()
);

-- Index biar query cepat
create index comments_created_at_idx on public.comments (created_at desc);

-- Aktifkan Realtime (opsional tapi bagus)
alter publication supabase_realtime add table public.comments;

Klik Run. Setelah sukses, cek di Table Editor — tabel comments sudah ada.

Struktur kolom yang dibuat:

KolomTipeKeterangan
iduuidPrimary key otomatis
nametextNama pengirim
topictextTopik (opsional)
messagetextIsi komentar
avatar_seedtextUntuk pravatar.cc
created_attimestamptzWaktu otomatis

4. Atur Row Level Security (RLS) — penting!

Supabase default mengaktifkan RLS. Tanpa policy yang benar, frontend tidak bisa baca/tulis data.

Di SQL Editor jalankan:

-- Aktifkan RLS
alter table public.comments enable row level security;

-- Siapa saja boleh BACA semua komentar
create policy "Public read comments"
on public.comments for select
to anon, authenticated
using (true);

-- Siapa saja boleh INSERT komentar baru
create policy "Public insert comments"
on public.comments for insert
to anon, authenticated
with check (true);

-- (Opsional) hanya pemilik yang bisa hapus — butuh auth
-- create policy "Users delete own comments" ...
Policy di atas sangat terbuka (semua orang bisa baca + tulis). Cocok untuk demo komunitas terbuka. Untuk production serius, tambahkan Auth + rate limit / moderation.

5. Ambil Project URL & Anon Key

Buka Project Settings

Sidebar kiri → ikon gear Project Settings → API.

Salin dua nilai ini

  • Project URL — contoh: https://abcdefghijklmn.supabase.co
  • anon public key (bukan service_role!) — string panjang yang dimulai dengan eyJ...

Simpan sementara. Nanti dimasukkan ke file konfigurasi di website.

Jangan pernah memasukkan service_role key ke frontend. Key itu bisa bypass RLS dan berbahaya jika bocor.

6. Integrasi ke Silverhawk WebLearn

Ada dua cara: (A) pakai CDN Supabase JS, atau (B) fetch REST API langsung. Di bawah contoh yang paling mudah (CDN).

6.1 Tambah script Supabase di index.html

Sebelum tag </body> (atau sebelum main.js), tambahkan:

<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
<script>
  // Ganti dengan nilai dari dashboard Supabase kamu
  window.SUPABASE_URL = "https://YOUR_PROJECT_ID.supabase.co";
  window.SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....";
</script>
<script src="js/main.js"></script>

6.2 Ganti fungsi komentar di js/main.js

Ganti seluruh fungsi initComments dengan versi yang memakai Supabase (masih ada fallback ke localStorage jika Supabase belum dikonfigurasi):

/* ==========================================================================
   Community comments — Supabase + localStorage fallback
   ========================================================================== */
function initComments(seedComments) {
  const list = $("#commentList");
  const form = $("#commentForm");
  if (!list || !form) return;

  const STORAGE_KEY = "shwl-comments";
  const hasSupabase = window.supabase && window.SUPABASE_URL && window.SUPABASE_ANON_KEY
    && !window.SUPABASE_URL.includes("YOUR_PROJECT");

  let client = null;
  if (hasSupabase) {
    client = window.supabase.createClient(window.SUPABASE_URL, window.SUPABASE_ANON_KEY);
  }

  function avatarUrl(seed) {
    return `https://i.pravatar.cc/64?img=${seed}`;
  }

  function formatTime(iso) {
    if (!iso) return "baru saja";
    const d = new Date(iso);
    const now = new Date();
    const diff = (now - d) / 1000;
    if (diff < 60) return "baru saja";
    if (diff < 3600) return Math.floor(diff / 60) + " menit lalu";
    if (diff < 86400) return Math.floor(diff / 3600) + " jam lalu";
    return d.toLocaleDateString("id-ID", { day: "numeric", month: "short", year: "numeric" });
  }

  function render(comments) {
    list.innerHTML = comments
      .map(
        (c) => `
      <div class="comment">
        <img src="${avatarUrl(c.avatar_seed || c.avatarSeed || "1")}" alt="Avatar ${c.name}" loading="lazy" />
        <div class="comment-content">
          <div class="comment-head"><b>${c.name}</b><span>${formatTime(c.created_at || c.time)}</span></div>
          <p>${c.topic ? `<strong>#${c.topic}</strong> — ` : ""}${c.message}</p>
        </div>
      </div>`
      )
      .join("");
    list.scrollTop = list.scrollHeight;
  }

  async function loadFromSupabase() {
    const { data, error } = await client
      .from("comments")
      .select("*")
      .order("created_at", { ascending: true });
    if (error) {
      console.warn("[supabase] gagal load komentar", error);
      return null;
    }
    return data || [];
  }

  async function loadLocal() {
    const stored = JSON.parse(localStorage.getItem(STORAGE_KEY) || "null");
    return stored || (seedComments || []).map((c) => ({ ...c, avatar_seed: c.avatarSeed }));
  }

  async function bootComments() {
    let comments = [];
    if (client) {
      comments = (await loadFromSupabase()) || (await loadLocal());
    } else {
      comments = await loadLocal();
    }
    render(comments);

    // Realtime (jika Supabase aktif)
    if (client) {
      client
        .channel("public:comments")
        .on("postgres_changes", { event: "INSERT", schema: "public", table: "comments" }, (payload) => {
          comments.push(payload.new);
          render(comments);
        })
        .subscribe();
    }
  }

  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const name = $("#commentName").value.trim();
    const topic = $("#commentTopic").value.trim();
    const message = $("#commentMessage").value.trim();
    if (!name || !message) return;

    const newComment = {
      name,
      topic: topic || null,
      message,
      avatar_seed: String(Math.floor(Math.random() * 70) + 1)
    };

    if (client) {
      const { data, error } = await client.from("comments").insert([newComment]).select();
      if (error) {
        console.error("[supabase] gagal simpan", error);
        alert("Gagal mengirim komentar. Coba lagi.");
        return;
      }
      // Realtime akan menangani render, tapi kita bisa push manual juga
      if (data && data[0]) {
        // sudah di-handle realtime
      }
    } else {
      // Fallback localStorage
      const comments = await loadLocal();
      comments.push({
        ...newComment,
        time: "baru saja",
        avatarSeed: newComment.avatar_seed
      });
      localStorage.setItem(STORAGE_KEY, JSON.stringify(comments));
      render(comments);
    }
    form.reset();
  });

  bootComments();
}
Kode di atas backward-compatible: jika kamu belum isi SUPABASE_URL / ANON_KEY, website tetap jalan pakai localStorage seperti semula.

7. Deploy ulang ke GitHub Pages

Commit & push

Setelah mengubah index.html dan js/main.js, commit perubahan ke repository GitHub-mu.

git add index.html js/main.js
git commit -m "feat: integrasi komentar Supabase"
git push origin main

Tunggu GitHub Pages update

Biasanya 1–3 menit. Buka website-mu, coba kirim komentar. Cek di Supabase Dashboard → Table Editor apakah data masuk.

Uji realtime

Buka website di dua browser/tab berbeda. Kirim komentar di salah satu — harus muncul otomatis di tab lain tanpa refresh.

Troubleshooting umum

MasalahKemungkinan penyebabSolusi
Komentar tidak munculRLS policy belum dibuatJalankan SQL policy di langkah 4
Error CORS / networkURL/key salahCek Project URL & anon key
Insert gagalPolicy insert belum adaBuat policy “Public insert comments”
Realtime tidak jalanBelum alter publicationJalankan SQL realtime di langkah 3
Key bocor di GitHubAnon key memang public by designAman selama RLS ketat. Jangan push service_role

Alternatif gratis selain Supabase

Berikut 3 alternatif yang juga gratis dan cocok untuk website statis di GitHub Pages:

1. Firebase (Google)

Firestore Auth Realtime

Backend milik Google. Free tier (Spark plan) sangat longgar untuk traffic kecil-menengah.

  • Firestore = database dokumen NoSQL
  • Realtime updates built-in
  • Auth email/Google mudah
  • Dokumentasi & tutorial sangat banyak

Cocok jika: kamu sudah akrab ekosistem Google / butuh auth cepat.

Mulai: firebase.google.com

2. Appwrite

Open source Cloud & Self-host

Alternatif open-source yang mirip Supabase. Ada Cloud free tier dan bisa self-host.

  • Database, Auth, Storage, Functions
  • Realtime via WebSocket
  • SDK JS ringan
  • Bisa deploy ke Cloud Appwrite gratis

Cocok jika: ingin open-source + kontrol lebih besar.

Mulai: appwrite.io

3. PocketBase

Single binary SQLite Self-host

Backend sangat ringan (satu file binary). Database SQLite + realtime + auth.

  • Sangat mudah di-host (Railway, Render, Fly.io free tier, VPS murah)
  • Admin UI built-in
  • Realtime & file storage
  • Cocok untuk project kecil–menengah

Cocok jika: ingin self-host sederhana & hemat resource.

Mulai: pocketbase.io

Ringkasan pilihan:
• Ingin paling mirip Postgres + setup cepat → Supabase
• Ingin ekosistem Google & tutorial berlimpah → Firebase
• Ingin open-source cloud → Appwrite
• Ingin self-host super ringan → PocketBase

Langkah selanjutnya (opsional)