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:
| Fitur | Fungsi |
|---|---|
| Data Donatur | Simpan informasi orang yang berdonasi |
| Catatan Donasi | Record setiap donasi yang masuk |
| Data Penerima | Informasi penerima manfaat |
| Program Yayasan | Daftar 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
- 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:
| Istilah | Analogi Lemari | Penjelasan |
|---|---|---|
| Database | Lemari arsip | Tempat semua data disimpan |
| Table | Laci | Kelompok data sejenis |
| Row | Folder | Satu record/entry data |
| Column | Info di folder | Jenis informasi yang disimpan |
| Primary Key | Nomor folder | ID unik untuk setiap row |
| Foreign Key | Referensi ke folder lain | Penghubung antar tabel |
| Query | Perintah cari | Instruksi 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:
- Install software database di komputer atau server
- Konfigurasi lewat command line
- Bikin user dan permission
- Setup backup dan security
- 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:
| Fitur | Fungsi |
|---|---|
| Database PostgreSQL | Database siap pakai, tidak perlu install |
| Auto-generated API | Akses data dari frontend tanpa coding backend |
| Authentication | Sistem login/register built-in |
| Storage | Tempat upload file dan gambar |
| Real-time | Data 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
- Buka supabase.com
- Klik "Start your project"
- Sign up dengan GitHub (paling cepat) atau email
- Selesai, kamu sudah punya akun
Step 2: Buat Project Baru
Setelah login, kamu akan masuk ke dashboard.
- Klik "New Project"
- Pilih organization (biasanya nama kamu)
- Isi detail project:
| Field | Isi |
|---|---|
| Project name | dashboard-yayasan |
| Database password | Buat password yang kuat |
| Region | Southeast Asia (Singapore) |
- Klik "Create new project"
- 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:
- Klik Settings (icon gear)
- Klik API di sidebar
- 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:
| Tabel | Fungsi | Relasi |
|---|---|---|
donatur | Data orang yang berdonasi | - |
donasi | Catatan setiap donasi | → donatur |
program | Daftar program yayasan | - |
penerima | Data 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
- Klik Table Editor di sidebar kiri
- Klik "Create a new table"
- Isi nama tabel:
donatur - Matikan "Enable Row Level Security" dulu (nanti kita aktifkan setelah paham)
- Tambahkan kolom-kolom berikut:
| Column Name | Type | Default Value | Primary | Nullable |
|---|---|---|---|---|
id | uuid | gen_random_uuid() | ✅ | ❌ |
nama | text | - | ❌ | |
email | text | - | ✅ | |
phone | text | - | ✅ | |
alamat | text | - | ✅ | |
created_at | timestamptz | now() | ❌ |
- Klik Save
Tabel donatur sudah jadi.
Insert Data Pertama
Coba masukkan data untuk testing:
- Klik tabel
donatur - Klik "Insert row"
- Isi data:
- nama:
Budi Santoso - email:
[email protected] - phone:
081234567890 - alamat:
Jakarta Selatan
- nama:
- 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 Name | Type | Default Value | Primary | Note |
|---|---|---|---|---|
id | uuid | gen_random_uuid() | ✅ | |
donatur_id | uuid | - | FK ke donatur | |
jumlah | int8 | - | Dalam Rupiah | |
tanggal | date | now() | Tanggal donasi | |
metode | text | 'transfer' | transfer/cash/ewallet | |
keterangan | text | - | Opsional | |
created_at | timestamptz | now() |
Setup Foreign Key
Setelah tabel dibuat, kita hubungkan donatur_id ke tabel donatur:
- Klik kolom
donatur_id - Klik Edit column
- Scroll ke bagian Foreign Key Relation
- Pilih:
- Table:
donatur - Column:
id
- Table:
- 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 Name | Type | Default Value | Note |
|---|---|---|---|
id | uuid | gen_random_uuid() | Primary key |
nama | text | - | Nama program |
deskripsi | text | - | Penjelasan program |
target_dana | int8 | 0 | Target dana yang dibutuhkan |
terkumpul | int8 | 0 | Dana yang sudah terkumpul |
status | text | 'aktif' | aktif / selesai / ditunda |
created_at | timestamptz | now() |
Contoh data program:
- Beasiswa Anak Yatim (target: 50 juta)
- Pembangunan Masjid (target: 200 juta)
- Bantuan Modal UMKM (target: 100 juta)
Tabel Penerima
| Column Name | Type | Default Value | Note |
|---|---|---|---|
id | uuid | gen_random_uuid() | Primary key |
program_id | uuid | - | FK ke program |
nama | text | - | Nama penerima |
kebutuhan | text | - | Deskripsi kebutuhan |
jumlah_bantuan | int8 | 0 | Bantuan yang diterima |
status | text | 'pending' | pending / terbantu / selesai |
created_at | timestamptz | now() |
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:
- Buka tabel di Table Editor
- Klik tab RLS di bagian atas
- 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
- Buka tabel → tab RLS
- Klik "New Policy"
- Pilih template atau tulis custom
- Isi nama policy dan SQL condition
- 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:
| Kelas | Apa yang Dipelajari |
|---|---|
| Full-Stack Next.js + Supabase | Complete app dengan auth, CRUD, dashboard, deploy |
| React JS Complete | Frontend modern untuk bikin UI dashboard |
| Laravel E-commerce | Backend tradisional sebagai perbandingan |
Langkah Selanjutnya
- Selesaikan project dashboard yayasan — Praktikkan semua yang dipelajari di artikel ini
- Ambil kelas SQL gratis — Perkuat query skills
- Bikin 1-2 project lagi dengan Supabase — Repetisi membangun muscle memory
- 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