Belajar Supabase Tanpa Pusing: Panduan Database untuk Pemula Vibe Coding

Panduan lengkap belajar Supabase untuk pemula yang baru kenal vibe coding. Dengan analogi sederhana dan project dashboard pengelola yayasan, kamu akan paham cara kerja database tanpa pusing. Artikel ini ditulis berdasarkan pengalaman saya mengajar ratusan mahasiswa di BuildWithAngga, dengan pendekatan yang sudah terbukti berhasil untuk pemula.


Bagian 1: Opening

"Kak, database itu susah gak sih?"

Pertanyaan ini hampir selalu muncul di setiap kelas pemula yang saya ajar di BuildWithAngga. Entah itu kelas web development, mobile app, atau full-stack — pasti ada yang tanya soal database.

Dan jawaban saya selalu sama: "Tergantung cara belajarnya."

Kalau kamu belajar database dengan langsung baca dokumentasi teknis yang penuh istilah asing seperti normalization, foreign key constraints, ACID compliance — ya wajar pusing. Itu seperti belajar nyetir mobil dengan baca manual mesin 500 halaman dulu.

Tapi kalau belajar dengan analogi yang relatable dan langsung praktik bikin sesuatu yang nyata — ceritanya beda.

Yang Akan Kita Pelajari

Di artikel ini, saya akan tunjukkan bahwa database — khususnya Supabase — itu tidak menakutkan. Kita akan:

  • Pahami konsep database dengan analogi lemari arsip kantor
  • Kenalan dengan Supabase beserta kelebihan dan kekurangannya
  • Bikin project Dashboard Pengelola Yayasan dari nol
  • Dapat tips praktis yang langsung bisa kamu pakai

Project yang Akan Kita Bangun

Supaya belajarnya lebih konkret, kita akan bikin dashboard sederhana untuk mengelola yayasan. Fiturnya:

FiturFungsi
Data DonaturSimpan informasi orang yang berdonasi
Catatan DonasiRecord setiap donasi yang masuk
Data PenerimaInformasi penerima manfaat
Program YayasanDaftar program yang sedang berjalan

Kenapa project yayasan? Karena strukturnya cukup sederhana untuk dipahami pemula, tapi punya relasi antar data yang cukup untuk belajar konsep penting database.

Siap? Mari kita mulai dari yang paling dasar.


Bagian 2: Database Itu Apa Sih?

Sebelum masuk ke Supabase, kita perlu paham dulu apa itu database. Tenang, tidak akan ada definisi teknis yang bikin pusing.

Analogi: Lemari Arsip Kantor

Bayangkan kantor yayasan punya lemari arsip besar untuk menyimpan semua dokumen penting.

LEMARI ARSIP = DATABASE

Ini adalah tempat sentral untuk menyimpan semua data yayasan. Semua informasi ada di sini, terorganisir dengan rapi.

LACI-LACI = TABEL

Di dalam lemari, ada beberapa laci dengan label berbeda:

  • Laci "Donatur" → berisi data orang-orang yang pernah donasi
  • Laci "Donasi" → berisi catatan setiap transaksi donasi
  • Laci "Penerima" → berisi data penerima bantuan
  • Laci "Program" → berisi daftar program yayasan

FOLDER DALAM LACI = ROW (Baris Data)

Di dalam setiap laci, ada folder-folder. Setiap folder adalah satu record. Misalnya di laci Donatur:

  • Folder "Budi Santoso"
  • Folder "Ani Wijaya"
  • Folder "Citra Dewi"

Setiap folder berisi informasi lengkap tentang satu donatur.

INFORMASI DI FOLDER = COLUMN (Kolom)

Setiap folder punya informasi yang konsisten:

  • Nama
  • Email
  • Nomor HP
  • Alamat
  • Tanggal pertama donasi

Visualisasi Struktur

Kalau digambarkan, struktur database yayasan kita seperti ini:

DATABASE: yayasan_db
│
├── TABEL: donatur
│   ┌─────┬───────────────┬──────────────────┬─────────────┐
│   │ id  │ nama          │ email            │ phone       │
│   ├─────┼───────────────┼──────────────────┼─────────────┤
│   │ 1   │ Budi Santoso  │ [email protected]   │ 0812xxxxx   │
│   │ 2   │ Ani Wijaya    │ [email protected]    │ 0813xxxxx   │
│   │ 3   │ Citra Dewi    │ [email protected]  │ 0857xxxxx   │
│   └─────┴───────────────┴──────────────────┴─────────────┘
│
├── TABEL: donasi
│   ┌─────┬────────────┬──────────┬────────────┐
│   │ id  │ donatur_id │ jumlah   │ tanggal    │
│   ├─────┼────────────┼──────────┼────────────┤
│   │ 1   │ 1          │ 500000   │ 2025-01-15 │
│   │ 2   │ 2          │ 1000000  │ 2025-01-20 │
│   │ 3   │ 1          │ 750000   │ 2025-02-10 │
│   └─────┴────────────┴──────────┴────────────┘
│
└── TABEL: penerima
    ┌─────┬─────────────┬─────────────────┬────────┐
    │ id  │ nama        │ kebutuhan       │ status │
    ├─────┼─────────────┼─────────────────┼────────┤
    │ 1   │ Keluarga A  │ Biaya sekolah   │ aktif  │
    │ 2   │ Keluarga B  │ Modal usaha     │ aktif  │
    └─────┴─────────────┴─────────────────┴────────┘

Perhatikan kolom donatur_id di tabel donasi. Angka 1 merujuk ke Budi Santoso di tabel donatur. Ini yang disebut relasi — data di satu tabel terhubung dengan data di tabel lain.

Kenapa Perlu Database?

Mungkin kamu berpikir, "Pakai Excel aja bisa kan?"

Bisa. Tapi ada batasannya.

Tanpa database (pakai spreadsheet):

  • Data tersebar di banyak file berbeda
  • Susah cari data spesifik dari ribuan baris
  • Rawan duplikasi dan inkonsistensi
  • Tidak bisa diakses bersamaan oleh banyak orang
  • Keamanan data minimal

Dengan database:

  • Data terpusat di satu tempat
  • Bisa query dengan cepat ("tampilkan donatur dari Jakarta yang donasi lebih dari 1 juta")
  • Ada relasi antar data yang terjaga
  • Multi-user access secara bersamaan
  • Sistem keamanan dan backup yang proper

Untuk aplikasi sederhana dengan data sedikit, spreadsheet memang cukup. Tapi begitu data mulai banyak dan butuh diakses dari aplikasi web/mobile, database jadi kebutuhan.

💡 MINI TIP:

Jangan langsung mikir "database = coding rumit".
Database modern seperti Supabase punya tampilan visual.
Kamu bisa kelola data seperti pakai spreadsheet,
tapi dengan kekuatan database di belakangnya.

Istilah yang Perlu Diingat

Sebelum lanjut, ini beberapa istilah yang akan sering muncul:

IstilahAnalogi LemariPenjelasan
DatabaseLemari arsipTempat semua data disimpan
TableLaciKelompok data sejenis
RowFolderSatu record/entry data
ColumnInfo di folderJenis informasi yang disimpan
Primary KeyNomor folderID unik untuk setiap row
Foreign KeyReferensi ke folder lainPenghubung antar tabel
QueryPerintah cariInstruksi untuk ambil/ubah data

Tidak perlu dihafal sekarang. Nanti akan lebih paham seiring praktik.


Di bagian selanjutnya, kita akan kenalan dengan Supabase — tools yang akan kita pakai untuk bikin database yayasan ini tanpa perlu setup server sendiri.


Bagian 3: Kenalan dengan Supabase

Sekarang kita sudah paham konsep database. Pertanyaan selanjutnya: gimana cara bikinnya?

Dulu, untuk punya database, kamu harus:

  1. Install software database di komputer atau server
  2. Konfigurasi lewat command line
  3. Bikin user dan permission
  4. Setup backup dan security
  5. Bikin API sendiri supaya bisa diakses dari aplikasi

Ribet? Memang. Makanya banyak pemula menyerah di tahap ini.

Tapi sekarang ada cara yang lebih mudah: Supabase.

Apa Itu Supabase?

Supabase adalah platform yang menyediakan:

FiturFungsi
Database PostgreSQLDatabase siap pakai, tidak perlu install
Auto-generated APIAkses data dari frontend tanpa coding backend
AuthenticationSistem login/register built-in
StorageTempat upload file dan gambar
Real-timeData update otomatis tanpa refresh

Semua ini tersedia dalam satu dashboard yang user-friendly. Kamu tinggal daftar, klik-klik, dan database sudah siap dipakai.

Kelebihan dan Kekurangan

Sebelum pakai tools apapun, penting untuk tau plus minusnya. Ini bukan untuk menakut-nakuti, tapi supaya kamu bisa memilih dengan tepat.

┌─────────────────────────────────────────────────────────┐
│              PROS & CONS SUPABASE                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✅ PROS:                                               │
│  ├── Gratis untuk project kecil (500MB storage)        │
│  ├── Tampilan visual, tidak harus coding SQL           │
│  ├── PostgreSQL (database profesional)                 │
│  ├── API langsung jadi tanpa coding backend            │
│  ├── Dokumentasi lengkap dan komunitas aktif           │
│  └── Real-time subscriptions built-in                  │
│                                                         │
│  ❌ CONS:                                               │
│  ├── Butuh koneksi internet (cloud-based)              │
│  ├── Free tier ada batas, perlu upgrade untuk scale    │
│  ├── Vendor lock-in (agak terikat platform)            │
│  ├── Query kompleks tetap butuh paham SQL              │
│  └── Server di luar Indonesia (latency)                │
│                                                         │
└─────────────────────────────────────────────────────────┘

Kapan Cocok Pakai Supabase?

Cocok untuk:

  • Project belajar dan eksperimen
  • Portfolio untuk lamar kerja
  • MVP dan prototype
  • Aplikasi skala kecil sampai menengah
  • Tim kecil yang butuh hasil cepat

Kurang cocok untuk:

  • Aplikasi yang harus jalan offline
  • Data sangat sensitif (perbankan, rumah sakit)
  • Butuh kontrol penuh atas infrastruktur
  • Project dengan budget sangat terbatas untuk scale up

Untuk dashboard yayasan yang akan kita bikin, Supabase sangat cocok. Data tidak terlalu sensitif, skala kecil-menengah, dan kita butuh hasil cepat untuk belajar.

💡 MINI TIP:

Untuk pemula, jangan terlalu pusing soal "nanti kalau scale gimana".
Fokus dulu bikin sesuatu yang jalan dan berfungsi.
Migrasi ke solusi lain bisa dilakukan nanti kalau memang perlu.

Bagian 4: Setup Project Yayasan

Sekarang waktunya praktik. Kita akan setup Supabase dan bikin struktur database untuk dashboard yayasan.

Step 1: Buat Akun Supabase

  1. Buka supabase.com
  2. Klik "Start your project"
  3. Sign up dengan GitHub (paling cepat) atau email
  4. Selesai, kamu sudah punya akun

Step 2: Buat Project Baru

Setelah login, kamu akan masuk ke dashboard.

  1. Klik "New Project"
  2. Pilih organization (biasanya nama kamu)
  3. Isi detail project:
FieldIsi
Project namedashboard-yayasan
Database passwordBuat password yang kuat
RegionSoutheast Asia (Singapore)
  1. Klik "Create new project"
  2. Tunggu sekitar 2 menit
⚠️ PENTING:

Password database ini tidak bisa di-recover kalau lupa.
Simpan di tempat yang aman sekarang juga.
Pakai password manager atau catat di notes yang secure.

Step 3: Kenali Dashboard

Setelah project siap, kamu akan melihat dashboard seperti ini:

┌─────────────────────────────────────────────────────────┐
│  dashboard-yayasan                          [Settings]  │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📊 Table Editor    → Bikin dan kelola tabel           │
│  📝 SQL Editor      → Tulis query SQL manual           │
│  🔐 Authentication  → Kelola user dan login            │
│  📁 Storage         → Upload file dan gambar           │
│  🔗 API             → Lihat dokumentasi API            │
│  ⚙️ Project Settings → API keys dan konfigurasi        │
│                                                         │
└─────────────────────────────────────────────────────────┘

Yang paling sering kita pakai: Table Editor dan API.

Step 4: Ambil API Keys

Untuk menghubungkan aplikasi frontend ke Supabase nanti, kita butuh:

  • Project URL
  • Anon Key (public key)

Caranya:

  1. Klik Settings (icon gear)
  2. Klik API di sidebar
  3. Copy Project URL dan anon public key

Simpan kedua nilai ini. Nanti akan kita pakai.

💡 MINI TIP:

Ada 2 jenis API key di Supabase:
- anon key → Aman untuk frontend, dibatasi oleh security rules
- service_role key → Bypass semua security, JANGAN pakai di frontend

Untuk project ini, kita hanya pakai anon key.

Step 5: Planning Struktur Tabel

Sebelum bikin tabel, kita rencanakan dulu strukturnya. Untuk dashboard yayasan, kita butuh 4 tabel:

TabelFungsiRelasi
donaturData orang yang berdonasi-
donasiCatatan setiap donasi→ donatur
programDaftar program yayasan-
penerimaData penerima manfaat→ program

ERD (Entity Relationship Diagram)

Ini gambaran visual hubungan antar tabel:

┌─────────────────┐           ┌─────────────────┐
│     DONATUR     │           │     PROGRAM     │
├─────────────────┤           ├─────────────────┤
│ id (PK)         │           │ id (PK)         │
│ nama            │           │ nama            │
│ email           │           │ deskripsi       │
│ phone           │           │ target_dana     │
│ alamat          │           │ status          │
│ created_at      │           │ created_at      │
└────────┬────────┘           └────────┬────────┘
         │                             │
         │ 1 (satu donatur)            │ 1 (satu program)
         │                             │
         │                             │
         N (banyak donasi)             N (banyak penerima)
         │                             │
┌────────┴────────┐           ┌────────┴────────┐
│     DONASI      │           │    PENERIMA     │
├─────────────────┤           ├─────────────────┤
│ id (PK)         │           │ id (PK)         │
│ donatur_id (FK) │           │ program_id (FK) │
│ jumlah          │           │ nama            │
│ tanggal         │           │ kebutuhan       │
│ metode          │           │ jumlah_bantuan  │
│ keterangan      │           │ status          │
│ created_at      │           │ created_at      │
└─────────────────┘           └─────────────────┘

Keterangan:
PK = Primary Key (ID unik)
FK = Foreign Key (referensi ke tabel lain)
1-N = Relasi satu ke banyak

Cara baca:

  • Satu donatur bisa punya banyak donasi (1 → N)
  • Satu program bisa punya banyak penerima (1 → N)
💡 MINI TIP:

Selalu gambar struktur database sebelum mulai bikin.
Bisa di kertas, di whiteboard, atau pakai tools seperti dbdiagram.io.
Lebih mudah ubah rencana daripada ubah database yang sudah terisi data.

Kenapa Strukturnya Seperti Ini?

Beberapa keputusan desain yang perlu dipahami:

1. Kenapa donasi dipisah dari donatur?

Karena satu donatur bisa donasi berkali-kali. Kalau digabung, data jadi duplikat dan susah di-maintain.

❌ SALAH (data duplikat):
| nama  | email        | donasi_1 | donasi_2 | donasi_3 |
|-------|--------------|----------|----------|----------|
| Budi  | [email protected]| 500000   | 750000   | NULL     |

✅ BENAR (dinormalisasi):
donatur: Budi, [email protected]
donasi: 500000 (ke Budi), 750000 (ke Budi)

2. Kenapa ada created_at di setiap tabel?

Untuk tracking kapan data dibuat. Berguna untuk audit dan laporan.

3. Kenapa pakai uuid bukan angka biasa untuk ID?

UUID lebih secure karena tidak bisa ditebak urutannya. Kalau pakai angka (1, 2, 3...), orang bisa nebak ID lain dengan mudah.


Di bagian selanjutnya, kita akan mulai bikin tabel-tabel ini di Supabase dengan langkah detail dan contoh kode untuk setiap operasi.


Bagian 5: Bikin Tabel Donatur

Sekarang kita mulai bikin tabel pertama: donatur.

Step-by-Step di Supabase

  1. Klik Table Editor di sidebar kiri
  2. Klik "Create a new table"
  3. Isi nama tabel: donatur
  4. Matikan "Enable Row Level Security" dulu (nanti kita aktifkan setelah paham)
  5. Tambahkan kolom-kolom berikut:
Column NameTypeDefault ValuePrimaryNullable
iduuidgen_random_uuid()
namatext-
emailtext-
phonetext-
alamattext-
created_attimestamptznow()
  1. Klik Save

Tabel donatur sudah jadi.

Insert Data Pertama

Coba masukkan data untuk testing:

  1. Klik tabel donatur
  2. Klik "Insert row"
  3. Isi data:
  4. Klik Save

Tambahkan 2-3 donatur lagi supaya ada data untuk testing.

Contoh Kode: Insert Donatur

Kalau nanti mau insert dari aplikasi frontend:

// Import supabase client (setup di file terpisah)
import { supabase } from './lib/supabase'

// Fungsi untuk tambah donatur baru
async function tambahDonatur(dataDonatur) {
  const { data, error } = await supabase
    .from('donatur')
    .insert({
      nama: dataDonatur.nama,
      email: dataDonatur.email,
      phone: dataDonatur.phone,
      alamat: dataDonatur.alamat
    })
    .select()
    .single()

  if (error) {
    console.error('Gagal menambah donatur:', error.message)
    return null
  }

  console.log('Donatur berhasil ditambahkan:', data)
  return data
}

// Cara pakai
const donaturBaru = await tambahDonatur({
  nama: 'Ani Wijaya',
  email: '[email protected]',
  phone: '081345678901',
  alamat: 'Bandung'
})

Contoh Kode: Ambil Data Donatur

// Ambil semua donatur
async function semuaDonatur() {
  const { data, error } = await supabase
    .from('donatur')
    .select('*')
    .order('created_at', { ascending: false })

  if (error) {
    console.error('Gagal mengambil data:', error.message)
    return []
  }

  return data
}

// Ambil donatur berdasarkan ID
async function donaturById(id) {
  const { data, error } = await supabase
    .from('donatur')
    .select('*')
    .eq('id', id)
    .single()

  return data
}

// Cari donatur berdasarkan nama
async function cariDonatur(keyword) {
  const { data, error } = await supabase
    .from('donatur')
    .select('*')
    .ilike('nama', `%${keyword}%`)

  return data
}
💡 MINI TIP:

Gunakan .single() kalau yakin hasilnya cuma 1 row.
Ini akan return object langsung, bukan array.

Tanpa .single(): [{ id: 1, nama: 'Budi' }]
Dengan .single(): { id: 1, nama: 'Budi' }

Bagian 6: Bikin Tabel Donasi dengan Relasi

Sekarang tabel kedua: donasi. Tabel ini punya relasi ke tabel donatur.

Struktur Tabel Donasi

Buat tabel baru dengan cara yang sama:

Column NameTypeDefault ValuePrimaryNote
iduuidgen_random_uuid()
donatur_iduuid-FK ke donatur
jumlahint8-Dalam Rupiah
tanggaldatenow()Tanggal donasi
metodetext'transfer'transfer/cash/ewallet
keterangantext-Opsional
created_attimestamptznow()

Setup Foreign Key

Setelah tabel dibuat, kita hubungkan donatur_id ke tabel donatur:

  1. Klik kolom donatur_id
  2. Klik Edit column
  3. Scroll ke bagian Foreign Key Relation
  4. Pilih:
    • Table: donatur
    • Column: id
  5. Klik Save

Sekarang setiap donasi harus terhubung ke donatur yang valid. Kalau coba insert dengan donatur_id yang tidak ada, akan error.

Contoh Kode: Insert Donasi

// Catat donasi baru
async function catatDonasi(dataDonasi) {
  const { data, error } = await supabase
    .from('donasi')
    .insert({
      donatur_id: dataDonasi.donaturId,
      jumlah: dataDonasi.jumlah,
      tanggal: dataDonasi.tanggal,
      metode: dataDonasi.metode,
      keterangan: dataDonasi.keterangan
    })
    .select()
    .single()

  if (error) {
    console.error('Gagal mencatat donasi:', error.message)
    return null
  }

  return data
}

// Cara pakai
const donasi = await catatDonasi({
  donaturId: 'uuid-budi-santoso',
  jumlah: 500000,
  tanggal: '2025-03-02',
  metode: 'transfer',
  keterangan: 'Donasi bulanan'
})

Query dengan Relasi (JOIN)

Ini bagian yang powerful. Dengan satu query, kita bisa ambil data donasi beserta data donatur-nya:

// Ambil donasi lengkap dengan info donatur
async function donasiLengkap() {
  const { data, error } = await supabase
    .from('donasi')
    .select(`
      id,
      jumlah,
      tanggal,
      metode,
      keterangan,
      donatur (
        id,
        nama,
        email
      )
    `)
    .order('tanggal', { ascending: false })

  return data
}

// Hasil yang didapat:
// [
//   {
//     id: 'uuid-donasi-1',
//     jumlah: 500000,
//     tanggal: '2025-03-02',
//     metode: 'transfer',
//     keterangan: 'Donasi bulanan',
//     donatur: {
//       id: 'uuid-budi',
//       nama: 'Budi Santoso',
//       email: '[email protected]'
//     }
//   },
//   ...
// ]

Perhatikan bagian donatur (id, nama, email). Ini cara Supabase melakukan JOIN otomatis berdasarkan foreign key yang sudah kita setup.

Query Donasi per Donatur

// Ambil semua donasi dari donatur tertentu
async function donasiByDonatur(donaturId) {
  const { data, error } = await supabase
    .from('donasi')
    .select('*')
    .eq('donatur_id', donaturId)
    .order('tanggal', { ascending: false })

  return data
}

// Hitung total donasi dari satu donatur
async function totalDonasiDonatur(donaturId) {
  const { data } = await supabase
    .from('donasi')
    .select('jumlah')
    .eq('donatur_id', donaturId)

  const total = data.reduce((sum, d) => sum + d.jumlah, 0)
  return total
}

Query Aggregate untuk Dashboard

// Statistik untuk dashboard
async function statistikDonasi() {
  // Total semua donasi
  const { data: semuaDonasi } = await supabase
    .from('donasi')
    .select('jumlah')

  const totalDonasi = semuaDonasi.reduce((sum, d) => sum + d.jumlah, 0)
  const jumlahTransaksi = semuaDonasi.length

  // Donasi bulan ini
  const awalBulan = new Date()
  awalBulan.setDate(1)
  awalBulan.setHours(0, 0, 0, 0)

  const { data: donasiBulanIni } = await supabase
    .from('donasi')
    .select('jumlah')
    .gte('tanggal', awalBulan.toISOString().split('T')[0])

  const totalBulanIni = donasiBulanIni.reduce((sum, d) => sum + d.jumlah, 0)

  return {
    totalDonasi,
    jumlahTransaksi,
    totalBulanIni,
    rataRata: Math.round(totalDonasi / jumlahTransaksi)
  }
}
💡 MINI TIP:

Relasi antar tabel itu yang membedakan database dari spreadsheet.
Dengan satu query, kamu bisa ambil data dari beberapa tabel sekaligus.
Tidak perlu query berkali-kali lalu gabungkan manual di JavaScript.

Pros dan Cons Relasi Database

┌─────────────────────────────────────────────────────────┐
│           PROS & CONS RELASI DATABASE                   │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✅ PROS:                                               │
│  ├── Data tidak duplikat (hemat storage)               │
│  ├── Update di satu tempat, otomatis konsisten         │
│  ├── Query powerful dengan JOIN                        │
│  ├── Data integrity terjaga (FK constraint)            │
│  └── Lebih mudah di-maintain jangka panjang            │
│                                                         │
│  ❌ CONS:                                               │
│  ├── Perlu planning di awal                            │
│  ├── Query bisa kompleks untuk pemula                  │
│  ├── Perlu paham konsep relasi                         │
│  └── Perubahan struktur butuh migrasi                  │
│                                                         │
└─────────────────────────────────────────────────────────┘

Di bagian selanjutnya, kita akan bikin tabel program dan penerima, lalu setup Row Level Security untuk keamanan data.


Bagian 7: Tabel Program dan Penerima

Dua tabel terakhir: program (daftar program yayasan) dan penerima (data penerima manfaat).

Tabel Program

Buat tabel dengan struktur berikut:

Column NameTypeDefault ValueNote
iduuidgen_random_uuid()Primary key
namatext-Nama program
deskripsitext-Penjelasan program
target_danaint80Target dana yang dibutuhkan
terkumpulint80Dana yang sudah terkumpul
statustext'aktif'aktif / selesai / ditunda
created_attimestamptznow()

Contoh data program:

  • Beasiswa Anak Yatim (target: 50 juta)
  • Pembangunan Masjid (target: 200 juta)
  • Bantuan Modal UMKM (target: 100 juta)

Tabel Penerima

Column NameTypeDefault ValueNote
iduuidgen_random_uuid()Primary key
program_iduuid-FK ke program
namatext-Nama penerima
kebutuhantext-Deskripsi kebutuhan
jumlah_bantuanint80Bantuan yang diterima
statustext'pending'pending / terbantu / selesai
created_attimestamptznow()

Jangan lupa setup foreign key program_id ke tabel program.

Contoh Query untuk Dashboard

// Ringkasan dashboard yayasan
async function getDashboardSummary() {
  // Total donatur
  const { count: totalDonatur } = await supabase
    .from('donatur')
    .select('*', { count: 'exact', head: true })

  // Total donasi keseluruhan
  const { data: semuaDonasi } = await supabase
    .from('donasi')
    .select('jumlah')

  const totalDonasi = semuaDonasi.reduce((sum, d) => sum + d.jumlah, 0)

  // Program aktif
  const { count: programAktif } = await supabase
    .from('program')
    .select('*', { count: 'exact', head: true })
    .eq('status', 'aktif')

  // Penerima yang sudah terbantu
  const { count: penerimaTerbantu } = await supabase
    .from('penerima')
    .select('*', { count: 'exact', head: true })
    .eq('status', 'terbantu')

  return {
    totalDonatur,
    totalDonasi,
    programAktif,
    penerimaTerbantu
  }
}

Query Program dengan Penerima

// Ambil program beserta daftar penerima
async function programDenganPenerima(programId) {
  const { data, error } = await supabase
    .from('program')
    .select(`
      id,
      nama,
      deskripsi,
      target_dana,
      terkumpul,
      status,
      penerima (
        id,
        nama,
        kebutuhan,
        jumlah_bantuan,
        status
      )
    `)
    .eq('id', programId)
    .single()

  return data
}

// Hasil:
// {
//   id: 'uuid-program',
//   nama: 'Beasiswa Anak Yatim',
//   target_dana: 50000000,
//   terkumpul: 35000000,
//   penerima: [
//     { nama: 'Ahmad', kebutuhan: 'Biaya sekolah SD', status: 'terbantu' },
//     { nama: 'Fatimah', kebutuhan: 'Biaya sekolah SMP', status: 'pending' }
//   ]
// }
💡 MINI TIP:

Gunakan { count: 'exact', head: true } kalau cuma butuh jumlah data.
- count: 'exact' → hitung jumlah row
- head: true → tidak ambil data, hanya count

Ini jauh lebih efisien daripada ambil semua data lalu hitung length-nya.

Bagian 8: Row Level Security (RLS)

Sekarang bagian penting: keamanan data.

Kenapa RLS Penting?

Secara default, siapapun yang punya API key bisa mengakses semua data. API key Supabase (anon key) itu public — bisa dilihat di frontend code.

Tanpa RLS:

  • Siapapun bisa lihat semua data donatur
  • Siapapun bisa edit atau hapus data
  • Tidak ada kontrol akses

Dengan RLS:

  • Kamu tentukan siapa boleh akses apa
  • Aturan diterapkan di level database
  • Tidak bisa di-bypass dari frontend

Enable RLS

Untuk setiap tabel:

  1. Buka tabel di Table Editor
  2. Klik tab RLS di bagian atas
  3. Klik "Enable RLS"

Setelah RLS aktif, semua akses diblokir sampai kamu buat policy.

Contoh Policy untuk Dashboard Yayasan

Policy 1: Program bisa dilihat publik

Untuk landing page donasi, orang perlu lihat program yang aktif tanpa login.

-- Semua orang bisa lihat program aktif
CREATE POLICY "Public can view active programs"
ON program
FOR SELECT
USING (status = 'aktif');

Policy 2: Donatur hanya untuk admin

Data donatur sensitif, hanya admin yang boleh akses.

-- Hanya authenticated user yang bisa lihat donatur
CREATE POLICY "Authenticated can view donatur"
ON donatur
FOR SELECT
TO authenticated
USING (true);

-- Hanya authenticated user yang bisa insert donatur
CREATE POLICY "Authenticated can insert donatur"
ON donatur
FOR INSERT
TO authenticated
WITH CHECK (true);

Policy 3: Donasi hanya untuk admin

-- Authenticated user bisa manage donasi
CREATE POLICY "Authenticated can manage donasi"
ON donasi
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);

Cara Tambah Policy di Supabase

  1. Buka tabel → tab RLS
  2. Klik "New Policy"
  3. Pilih template atau tulis custom
  4. Isi nama policy dan SQL condition
  5. Klik Save

Atau lewat SQL Editor:

-- Jalankan di SQL Editor
CREATE POLICY "nama_policy"
ON nama_tabel
FOR SELECT  -- atau INSERT, UPDATE, DELETE, ALL
TO authenticated  -- atau anon, atau role tertentu
USING (kondisi);

Testing RLS

Setelah setup RLS, test untuk memastikan berfungsi:

// Test tanpa login (anon)
const { data: programPublic } = await supabase
  .from('program')
  .select('*')
// Harusnya dapat data program aktif

const { data: donaturPublic } = await supabase
  .from('donatur')
  .select('*')
// Harusnya kosong atau error (blocked by RLS)
💡 MINI TIP:

Mulai dengan policy sederhana dulu:
1. Enable RLS
2. Buat policy untuk authenticated users
3. Test apakah berfungsi

Policy yang lebih kompleks bisa ditambahkan seiring kebutuhan.
Jangan over-engineer di awal.

Pros dan Cons RLS

┌─────────────────────────────────────────────────────────┐
│              PROS & CONS ROW LEVEL SECURITY             │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✅ PROS:                                               │
│  ├── Keamanan di level database (paling aman)          │
│  ├── Tidak bisa di-bypass dari frontend                │
│  ├── Satu tempat untuk semua aturan akses              │
│  ├── Otomatis berlaku untuk semua query                │
│  └── Audit trail lebih mudah                           │
│                                                         │
│  ❌ CONS:                                               │
│  ├── Perlu paham SQL untuk policy kompleks             │
│  ├── Debugging bisa tricky kalau salah setup           │
│  ├── Performance bisa turun kalau policy terlalu berat │
│  └── Learning curve untuk pemula                       │
│                                                         │
└─────────────────────────────────────────────────────────┘

Setup Authentication Sederhana

Supaya RLS dengan authenticated berfungsi, user perlu login. Supabase punya auth built-in:

// Sign up user baru
const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'password-kuat-123'
})

// Login
const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'password-kuat-123'
})

// Logout
await supabase.auth.signOut()

// Cek user yang sedang login
const { data: { user } } = await supabase.auth.getUser()

Setelah login, semua request ke Supabase otomatis membawa token authentication. RLS akan mengenali user sebagai authenticated.


Di bagian terakhir, kita akan bahas best practices dan rekomendasi belajar lanjutan di BuildWithAngga.


Bagian 9: Best Practices dan Tips Tambahan

Sebelum menutup, ini beberapa praktik baik yang akan membantu kamu ke depannya.

Naming Convention

Konsistensi penamaan bikin code lebih mudah dibaca dan di-maintain.

✅ REKOMENDASI:

Tabel    → singular, lowercase (donatur, donasi, program)
Kolom    → snake_case (created_at, donatur_id, jumlah_bantuan)
FK       → nama_tabel_id (donatur_id, program_id)
Boolean  → prefix is_ atau has_ (is_active, has_donated)
Tanggal  → suffix _at atau _date (created_at, tanggal_donasi)

❌ HINDARI:

PascalCase    → Donatur, JumlahDonasi
camelCase     → createdAt (di database, pakai snake_case)
Prefix tbl_   → tbl_donatur (tidak perlu)
Nama ambigu   → data, info, item (kurang deskriptif)

Validasi Sebelum Insert

Jangan langsung insert data ke database. Validasi dulu di frontend.

// Fungsi validasi donasi
function validasiDonasi(data) {
  const errors = []

  if (!data.donatur_id) {
    errors.push('Donatur harus dipilih')
  }

  if (!data.jumlah || data.jumlah < 10000) {
    errors.push('Jumlah donasi minimal Rp 10.000')
  }

  if (!data.tanggal) {
    errors.push('Tanggal harus diisi')
  }

  return errors
}

// Pakai sebelum insert
async function submitDonasi(formData) {
  const errors = validasiDonasi(formData)

  if (errors.length > 0) {
    alert('Error:\\n' + errors.join('\\n'))
    return null
  }

  // Baru insert ke database
  const { data, error } = await supabase
    .from('donasi')
    .insert(formData)
    .select()
    .single()

  return data
}

Error Handling yang Konsisten

Buat pattern yang sama untuk semua operasi database.

// Helper function untuk operasi database
async function dbOperation(operation) {
  try {
    const { data, error } = await operation

    if (error) {
      console.error('Database error:', error.message)
      return { success: false, error: error.message, data: null }
    }

    return { success: true, error: null, data }
  } catch (err) {
    console.error('Unexpected error:', err)
    return { success: false, error: 'Terjadi kesalahan', data: null }
  }
}

// Cara pakai
const result = await dbOperation(
  supabase.from('donatur').insert(formData).select().single()
)

if (result.success) {
  alert('Data berhasil disimpan')
} else {
  alert('Gagal: ' + result.error)
}

Gunakan Helper Functions

Daripada tulis query yang sama berulang-ulang, buat helper functions.

// lib/database/donatur.js

export async function getAllDonatur() {
  const { data, error } = await supabase
    .from('donatur')
    .select('*')
    .order('created_at', { ascending: false })

  return data || []
}

export async function getDonaturById(id) {
  const { data } = await supabase
    .from('donatur')
    .select('*')
    .eq('id', id)
    .single()

  return data
}

export async function createDonatur(donaturData) {
  const { data, error } = await supabase
    .from('donatur')
    .insert(donaturData)
    .select()
    .single()

  if (error) throw new Error(error.message)
  return data
}

export async function updateDonatur(id, updates) {
  const { data, error } = await supabase
    .from('donatur')
    .update(updates)
    .eq('id', id)
    .select()
    .single()

  if (error) throw new Error(error.message)
  return data
}

export async function deleteDonatur(id) {
  const { error } = await supabase
    .from('donatur')
    .delete()
    .eq('id', id)

  if (error) throw new Error(error.message)
  return true
}
💡 MINI TIP:

Dengan helper functions, komponen React kamu jadi lebih bersih.
Tinggal panggil getAllDonatur() tanpa perlu tau detail query-nya.
Kalau nanti perlu ubah logic, cukup edit di satu tempat.

Bagian 10: Rekomendasi Belajar Lanjutan

Sekarang kamu sudah punya fondasi yang solid tentang database dan Supabase. Ini recap perjalanan kita:

Yang Sudah Dipelajari

✅ Konsep database dengan analogi lemari arsip
✅ Apa itu Supabase dan kapan menggunakannya
✅ Bikin tabel: donatur, donasi, program, penerima
✅ Relasi antar tabel dengan foreign key
✅ Query dasar: insert, select, update, delete
✅ Query dengan JOIN untuk ambil data berelasi
✅ Row Level Security untuk keamanan
✅ Best practices coding

Ini fondasi yang bagus. Tapi untuk jadi developer yang solid, ada beberapa hal lagi yang perlu dipelajari.

Kelas Gratis di BuildWithAngga

Untuk memperkuat fundamental, ambil kelas gratis ini:

1. SQL for Beginners: Learn SQL using MySQL and Database Design

📚 Yang dipelajari:
├── SELECT, INSERT, UPDATE, DELETE dengan detail
├── WHERE, ORDER BY, LIMIT, OFFSET
├── JOIN (INNER, LEFT, RIGHT)
├── GROUP BY dan aggregate functions
├── Subquery dan nested query
└── Database design best practices

🔗 buildwithangga.com/kelas/sql-for-beginners
💰 Gratis

Konsep SQL sama untuk semua database. Ilmu dari kelas ini langsung applicable ke Supabase.

2. JavaScript Fundamentals

📚 Yang dipelajari:
├── Async/await (penting untuk API calls)
├── Array methods (map, filter, reduce)
├── Object manipulation
├── Error handling
├── ES6+ features
└── Module system

💰 Gratis

Karena Supabase client pakai JavaScript, pastikan fundamentalnya kuat.

3. React JS Fundamentals

📚 Yang dipelajari:
├── Component-based thinking
├── Props dan state
├── Hooks (useState, useEffect)
├── Event handling
├── Conditional rendering
└── List rendering

💰 Gratis

Kalau mau bikin dashboard yayasan dengan UI yang proper, React adalah pilihan populer.

Kenapa Lanjut ke Kelas Premium?

Kelas gratis bagus untuk fondasi. Tapi untuk skill yang job-ready, kelas premium menawarkan lebih:

┌─────────────────────────────────────────────────────────┐
│           BENEFIT KELAS PREMIUM BWA                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📦 PROJECT LENGKAP                                     │
│  ├── Bukan cuma teori, langsung bikin aplikasi nyata   │
│  ├── Dari setup sampai deploy                          │
│  └── Portfolio-ready untuk lamar kerja                 │
│                                                         │
│  ♾️ AKSES SEUMUR HIDUP                                  │
│  ├── Beli sekali, akses selamanya                      │
│  ├── Update materi gratis                              │
│  └── Bisa diulang kapanpun                             │
│                                                         │
│  👨‍🏫 KONSULTASI MENTOR                                  │
│  ├── Tanya langsung kalau stuck                        │
│  ├── Code review dari praktisi                         │
│  └── Feedback untuk improvement                        │
│                                                         │
│  📜 SERTIFIKAT                                          │
│  ├── Bukti completion resmi                            │
│  ├── Bisa ditambahkan ke LinkedIn                      │
│  └── Nilai plus untuk CV                               │
│                                                         │
│  👥 KOMUNITAS                                           │
│  ├── Network dengan sesama learner                     │
│  ├── Info lowongan dan freelance                       │
│  └── Diskusi dan sharing pengalaman                    │
│                                                         │
└─────────────────────────────────────────────────────────┘

Kelas Premium yang Relevan

Untuk melanjutkan dari artikel ini:

KelasApa yang Dipelajari
Full-Stack Next.js + SupabaseComplete app dengan auth, CRUD, dashboard, deploy
React JS CompleteFrontend modern untuk bikin UI dashboard
Laravel E-commerceBackend tradisional sebagai perbandingan

Langkah Selanjutnya

  1. Selesaikan project dashboard yayasan — Praktikkan semua yang dipelajari di artikel ini
  2. Ambil kelas SQL gratis — Perkuat query skills
  3. Bikin 1-2 project lagi dengan Supabase — Repetisi membangun muscle memory
  4. Upgrade ke premium — Kalau sudah siap untuk project yang lebih serius

Penutup

Database tidak harus pusing.

Dengan pendekatan yang tepat — analogi sederhana, project yang relevan, dan praktik langsung — siapapun bisa belajar.

Yang penting: mulai dari yang sederhana, konsisten, dan jangan takut untuk mencoba. Salah itu bagian dari proses belajar.

Supabase adalah alat yang bagus untuk memulai. Tapi ingat, ini baru alat bantu. Fundamental seperti SQL, JavaScript, dan konsep database tetap penting untuk dipelajari.

Semoga artikel ini membantu perjalanan belajarmu.

Sampai jumpa di kelas!


Angga Risky Founder, BuildWithAngga

👉 buildwithangga.com