[TUTORIAL] ⚡ Apa itu Supabase? Inilah Pengertian hingga Cara Membuat Database dan Aplikasi CRUD Sederhana! (+ Video Tutorial)

Assalamu‘alaikum wr. wb. 

Halo guys! Kembali lagi bersama Teknoblog di Inzaghi's Blog! Jika sebelumnya sudah membahas mengenai Perbandingan antara Firebase dan Supabase, kali ini kita akan membahas mengenai Cara Membuat Database Sederhana dengan Supabase.

[TUTORIAL] Apa itu Supabase? Inilah Pengertian hingga Cara Membuat Database dan Aplikasi CRUD Sederhana! (+ Video Tutorial)


PENGERTIAN

Sumber Artikel : Bejamas.comMiyagami.com (Blog)Blog.Lewagon.comHariankoding.com, dan Kumpulblogger.com (Blog)

Dalam konteks pengembangan web, basis data merupakan fondasi utama dari setiap aplikasi full-stack. Di sanalah data disimpan dan kemudian diambil kembali untuk ditampilkan kepada pengguna yang mengakses sebuah website. Namun, bagi banyak pengembang pemula, mengelola basis data sering terasa menakutkan. Istilah seperti kueri SQL, relasi data, hingga isu skalabilitas kerap terdengar rumit, terutama bagi mereka yang baru mulai belajar pemrograman.

Di sinilah peran alat seperti Supabase menjadi sangat relevan. Platform open-source ini semakin populer di kalangan developer karena menawarkan cara mengelola backend aplikasi full-stack yang ramah bagi pemula, namun tetap kuat dan fleksibel tanpa mengorbankan kontrol.

Baik untuk mengerjakan proyek sampingan maupun mengembangkan aplikasi berskala enterprise, Supabase menyediakan berbagai fitur yang dibutuhkan untuk sukses. Yang paling menarik, platform ini menyederhanakan pengelolaan basis data hingga mudah dipahami oleh siapa saja.

Melalui panduan ini, kita akan membahas apa saja yang membuat Supabase menjadi solusi revolusioner di dunia basis data, sekaligus menyoroti fitur-fitur utamanya agar siapa pun dapat memulai perjalanan mengelola database dengan lebih percaya diri.

A. Pengertian Supabase

Supabase merupakan layanan backend-as-a-service (BaaS) berbasis open-source yang dibuat untuk mempermudah dan mempercepat proses pengembangan aplikasi, tanpa harus menyusun backend dari awal. Platform ini menggunakan PostgreSQL sebagai fondasi utama dan menghadirkan berbagai fitur penting, seperti autentikasi pengguna, database real-time, penyimpanan berkas, hingga fungsi serverless.

Kepopuleran Supabase terus meningkat karena menawarkan paket backend yang siap pakai dengan proses konfigurasi yang sederhana. Pengembang tidak perlu lagi menangani pengaturan server secara manual atau berurusan dengan infrastruktur yang kompleks, karena seluruh kebutuhan backend dapat dikelola melalui satu dashboard yang user-friendly.

Di sisi lain, Supabase didukung oleh komunitas open-source yang aktif dan berkembang. Update rutin, dokumentasi yang jelas, serta dukungan integrasi yang semakin beragam membuat Supabase menjadi pilihan menarik, terutama bagi developer pemula yang ingin lebih fokus mengembangkan sisi frontend tanpa mengabaikan performa maupun skalabilitas backend.

Alasan Supabase begitu populer antara lain :

  • Tersedia gratis dan bersifat open-source, ideal untuk proyek kecil hingga menengah
  • Menggunakan PostgreSQL yang dikenal stabil dan andal
  • Mendukung fitur real-time secara bawaan
  • Memiliki dashboard modern yang mudah dioperasikan
  • Mendukung penggunaan RESTful API dan GraphQL

B. Fitur-fitur Inti Supabase

Supabase menyediakan beragam fitur yang dirancang untuk mempermudah proses pengembangan aplikasi, sehingga developer dapat lebih fokus pada logika dan inti fungsional aplikasi. Fitur-fitur yang ditawarkan mencakup berbagai aspek penting, di antaranya:

1. Autentikasi

Supabase dilengkapi dengan sistem autentikasi dan manajemen pengguna yang sudah siap pakai tanpa perlu integrasi dengan layanan pihak ketiga. Fitur ini memungkinkan pengembang melakukan verifikasi pengguna serta mengatur pembatasan akses dalam aplikasi. Metode autentikasi yang tersedia meliputi magic link, email, nomor telepon, Single Sign-On (SSO), serta berbagai login media sosial seperti Facebook, Google, Twitter, Slack, GitHub, dan lainnya.

Autentikasi di Supabase menggunakan OAuth 2.0 dan mendukung Login Email, tautan ajaib, serta penyedia pihak ketiga seperti Google, GitHub, dan Azure. Supabase Auth terintegrasi dengan basis data Anda, memberi kendali penuh atas data pengguna tanpa kotak hitam tersembunyi. Data sesi dan token disimpan di basis data Anda.

Untuk semakin menyederhanakan proses autentikasi, Supabase juga menyediakan komponen UI autentikasi bawaan untuk React, serta berbagai helper tambahan untuk framework seperti Next.js, Remix, dan SvelteKit.

Selain itu, Supabase menawarkan fitur autentikasi lanjutan yang memberikan fleksibilitas dan kustomisasi lebih luas, seperti :

  • Penyesuaian Template Email
  • Autentikasi multi-faktor (MFA)
  • Perlindungan Captcha

2. Database

Setiap proyek Supabase secara otomatis dibekali dengan database Postgres, sehingga pengembang dapat memanfaatkan seluruh kemampuan yang dimiliki Postgres. Fungsionalitas database dapat diperluas dengan menambahkan berbagai ekstensi hanya dengan beberapa klik.

Antarmuka tabel berbasis GUI yang ramah pengguna memudahkan siapa saja untuk melihat dan mengelola data, bahkan bagi pemula. Supabase juga menyederhanakan pengelolaan database dengan fitur seperti kloning tabel secara cepat, pembuatan relasi antar tabel, serta eksekusi query melalui SQL Editor. Proses pencadangan database ditangani otomatis oleh Supabase, sehingga mengurangi kompleksitas teknis. Selain itu, data dapat diimpor langsung ke tabel menggunakan file CSV atau Excel.

3. Database Realtime

Pembaruan data secara real-time dan sinkronisasi yang mulus sangat penting untuk membangun aplikasi yang interaktif. Supabase Realtime memungkinkan integrasi pembaruan data langsung ke dalam aplikasi. Misalnya, dengan mengaktifkan Postgres Changes, Anda dapat menerima pembaruan real-time atas operasi CRUD yang terjadi di database. Fitur Broadcast juga memungkinkan pengiriman pesan ke banyak klien sekaligus.

Dengan Realtime, pengembang dapat dengan mudah membangun aplikasi yang membutuhkan pembaruan langsung, memantau kehadiran pengguna dan status online, serta melacak latensi pesan.

4. Storage

Supabase menyediakan layanan penyimpanan file langsung dari platform, sehingga Anda dapat mengelola aplikasi secara terpusat tanpa perlu menggunakan layanan tambahan seperti AWS S3 dan PostgreSQL. Sistem ini memungkinkan penyimpanan file terhubung dengan database, meskipun penyimpanan file berukuran besar secara langsung di database tidak disarankan.

Sebagai solusinya, Supabase menawarkan storage bucket, yaitu folder khusus untuk menyimpan file dengan lebih efisien dan terstruktur.

5. Edge Functions

Edge Functions adalah fungsi yang dijalankan secara global di “tepi internet” dan berfungsi untuk menangani webhook serta integrasi dengan aplikasi pihak ketiga. Dengan Edge Functions, proyek Supabase dapat terhubung dengan berbagai layanan seperti Stripe, bot Telegram, OpenAI, dan lainnya. Contoh implementasi Edge Functions Supabase dapat ditemukan di repositori GitHub resmi mereka.

C. Paket Harga Supabase

Supabase menerapkan model penetapan harga berbasis penggunaan dengan batas sumber daya yang jelas dan transparan. Platform ini menyediakan paket gratis yang cukup besar, dirancang untuk kebutuhan pengembangan dan proyek skala kecil. Paket gratis tersebut sudah mencakup kapasitas database, penyimpanan file, bandwidth, serta permintaan autentikasi, sehingga pengembang dapat membangun dan merilis aplikasi tanpa harus langsung memikirkan biaya.

Supabase Pricing & Fees

Untuk paket berbayar, skema penagihan dibagi ke dalam dua aspek utama, yaitu sumber daya dasar dan daya komputasi. Sumber daya dasar meliputi penyimpanan database, penyimpanan file, bandwidth, serta jumlah permintaan API. Sementara itu, daya komputasi—yang menentukan performa pemrosesan instance PostgreSQL—dapat ditingkatkan secara terpisah sesuai kebutuhan aplikasi. Pemisahan ini membuat pengguna tidak perlu membayar lebih untuk komputasi jika yang dibutuhkan hanya tambahan kapasitas penyimpanan, begitu pula sebaliknya.

Keunggulan utama dari model harga Supabase terletak pada tingkat transparansinya. Seluruh metrik penggunaan dapat dipantau langsung melalui dashboard, mulai dari ukuran database, jumlah panggilan API, penggunaan bandwidth, hingga eksekusi fungsi yang ditampilkan secara real-time. Dengan demikian, risiko tagihan tak terduga dapat dihindari. Ketika mendekati batas paket, pengguna dapat melihat dengan jelas sumber daya apa yang paling banyak digunakan. Biaya kelebihan pemakaian juga dihitung secara konsisten, dan tersedia opsi pembatasan pengeluaran untuk mencegah lonjakan biaya.

Beberapa fitur tambahan seperti pencadangan harian, pemulihan data hingga titik waktu tertentu (point-in-time recovery), dan dukungan prioritas hanya tersedia pada paket berbayar. Namun, fitur inti seperti API otomatis, autentikasi, dan langganan real-time tetap bisa digunakan di semua tingkatan paket. Bagi organisasi yang membutuhkan infrastruktur khusus atau ingin melakukan self-hosting, Supabase juga menawarkan paket enterprise dengan SLA khusus, dukungan kepatuhan, serta proses onboarding premium. Karena Supabase bersifat open-source, pengguna juga memiliki opsi untuk menjalankan seluruh stack secara mandiri demi kontrol penuh atas biaya dan infrastruktur.

D. Kelebihan dan Kekurangan Menggunakan Supabase

Tidak ada satu pun platform yang benar-benar tanpa kekurangan, termasuk Supabase. Walaupun Supabase menghadirkan banyak fitur yang menarik dan memudahkan pengembangan aplikasi, tetap terdapat kelebihan dan keterbatasan yang perlu dipertimbangkan, khususnya jika akan digunakan untuk proyek berskala besar.

Kelebihan Supabase antara lain :

  • Bersifat open-source dan menyediakan paket gratis yang cocok untuk proyek kecil hingga menengah
  • Mendukung fitur real-time secara langsung tanpa konfigurasi yang rumit
  • Menggunakan PostgreSQL sehingga menawarkan fleksibilitas dan kekuatan database relasional
  • Memiliki dashboard dengan tampilan intuitif dan mudah digunakan oleh pemula
  • Dokumentasi yang lengkap serta didukung komunitas yang aktif

Di sisi lain, Supabase juga memiliki beberapa keterbatasan, seperti :

  • Fitur enterprise belum selengkap Firebase
  • Kurang optimal untuk aplikasi dengan trafik sangat tinggi dan skala besar
  • Edge Functions masih terbatas jika dibandingkan dengan layanan seperti AWS Lambda
  • Kurang cocok bagi developer yang lebih terbiasa menggunakan MongoDB

Oleh karena itu, pemilihan Supabase sebaiknya disesuaikan dengan kebutuhan dan skala proyek. Untuk pengembangan MVP, prototipe, maupun aplikasi kecil hingga menengah, Supabase merupakan solusi backend yang cukup andal dan efisien.




TUTORIAL

Sumber Tutorial : Supabase.com (Docs)Reactjs.Koida.tech (Hands-on: Note Taking App)Reactjs.Koida.tech (React Supabase CRUD), dan Nihardaily.com

Berikut, inilah Cara Membuat Database Sederhana dengan menggunakan Supabase. Untuk Project-nya, kita akan membuat Aplikasi To-do List Sederhana.

A. Daftar Akun dan Membuat Database di Supabase

Pertama, marilah kita buka terlebih dahulu Website Supabase di sini. Kemudian, klik Tombol "Sign in".

Jika belum punya Akun, klik "Sign up" terlebih dahulu.

Setelah itu, Sign up (Mendaftar) dengan menggunakan Akun Email (Google/Gmail) Anda dan setelah itu klik "Sign up".

Lalu, bukalah Email kalian dan Periksalah apakah sudah masuk untuk melakukan Verifikasi. Pada Email yang telah dikirim tadi, klik saja "Create Your First Project".

Jika sudah, buatlah salah satu Organisasi apapun. Bagi Pemula, boleh asal-asalan saja menamai Organisasi Baru ini, dan pilihlah Tipe-nya menjadi "Personal" dan juga pilih Free Plan. Setelah itu, klik "Create Organization".

Kemudian, buatlah Nama Project dan Password pada Database yang akan dibuat di Supabase. Untuk Region, pilih saja Wilayah Asia-Pasifik dan Centang pada bagian Security (Enable Data API). Setelah itu, klik Tombol "Create New Project". 

Jika sudah, maka akan seperti inilah Tampilan Awal dari Supabase.

Jika kita kembali ke Dashboard Organizations, maka akan seperti ini.

Namun terkadang, jika Project Anda muncul seperti ini, langsung saja klik "Resume Project". Mungkin saja, Pop up tersebut muncul karena Project Supabase kalian tidak pernah digunakan selama berhari-hari, bahkan sampai Lebih dari Sebulan.

Kemudian, klik lagi "Resume" untuk membuka dan melanjutkan Proyek Supabase Anda.

B. Membuat Proyek Sederhana di Supabase

Kali ini kita akan membuat Proyek Aplikasi To-do List Sederhana dengan Supabase dan menggunakan React.js + Vite. Adapun Teknologi yang Digunakan adalah sebagai berikut :

  • React
  • Vite
  • Supabase
  • PostgreSQL
  • Lucide React

Dan seperti inilah Struktur Folder untuk Project ini :

📁 todo-supabase
├── 📁 node_modules ├── 📁 public ├── favicon.svg
├── icons.svg ├── 📁 src
├── 📁 components ├── AddTodo.jsx ├── Auth.jsx
├── TodoList.jsx
├── App.jsx
├── index.css
├── main.jsx
├── supabaseClient.js ├── 📁 supabase ├── schema.sql ├── .env
├── .env.example
├── .gitignore ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── README.md ├── vite.config.js

1. Mencari API Credentials

Pertama, bukalah Dashboard Supabase dan bukalah pada Tampilan Project Overview.

Kemudian, klik "Project Settings".

Kemudian, carilah pada bagian "API Keys" dan juga pada "Publishable Key". Jadi, Publishable Key ini berfungsi untuk menyalin Kode Supabase Anon Key (VITE_SUPABASE_ANON_KEY) Anda di dalam File .env.

Untuk mencari Link Supabase URL, silakan klik pada bagian "Data API" di bagian Sidebar "Integrations", lalu carilah pada bagian API URL. Sedangkan, API URL ini untuk menyalin Link/Tautan pada bagian VITE_SUPABASE_URL di File .env.

2. Membuat Project React + Vite dan melakukan Kofigurasi Supabase

Pertama, buatlah Project React dan Vite terlebih dahulu dengan mengetik Perintah berikut ini :

npm create vite@latest todo-supabase -- --template react
cd todo-supabase
npm install

Kemudian, Install Supabase dan Icon Library :

npm install @supabase/supabase-js lucide-react

Jika sudah, maka buatlah File .env, dan isilah Filenya di sini :

VITE_SUPABASE_URL=your-supabase-url-here
VITE_SUPABASE_ANON_KEY=your-supabase-anon-key-here

Contoh :

VITE_SUPABASE_URL=https://xxxxx.supabase.co
VITE_SUPABASE_ANON_KEY=xxxxxxxxxxxxxxxx

Selanjutnya, membuat File Konfigurasi Supabase Client di sini, yang terletak di src/supabaseClient.js. Abaikan saja VITE_SUPABASE_URL dan VITE_SUPABASE_ANON_KEY, karena sudah ditulis di File .env tadi.

File supabaseClient.js digunakan untuk membuat koneksi antara aplikasi React dengan backend Supabase. File ini membaca environment variable dari .env seperti VITE_SUPABASE_URL dan VITE_SUPABASE_ANON_KEY, lalu menggunakan fungsi createClient() dari package @supabase/supabase-js untuk membuat instance Supabase client. Instance inilah yang nantinya digunakan di seluruh aplikasi untuk login, mengambil data, insert data, delete data, dan operasi database lainnya.

Inilah isi File dari supabaseClient.js :

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(
  supabaseUrl,
  supabaseAnonKey
)

3. Membuat Database Table

Sebenarnya, ada 2 (Dua) Cara untuk membuat Tabel Database di Supabase, yaitu dengan menggunakan Table Editor (Manual) dan SQL Editor (Kueri SQL).

Jika ingin menggunakan Kueri SQL, bukalah Dashboard-nya lagi dan klik pada bagian "SQL Editor". Ketiklah :

create extension if not exists "uuid-ossp";

create table tasks (
  id uuid primary key default uuid_generate_v4(),
  user_id uuid references auth.users(id),
  title text not null,
  created_at timestamp default now()
);

Setelah itu, klik Tombol "Run" untuk menjalankan Kueri SQL tersebut. Untuk Kode SQL pada Supabase sendiri menggunakan PostgreSQL (Postgres).

Jika muncul Peringatan seperti Gambar di bawah ini, berarti peringatan keamanan dari Supabase yang muncul karena Tabel yang kita buat di SQL Editor belum mengaktifkan Row Level Security (RLS).

Artinya, Supabase mendeteksi bahwa Query :

create table tasks (...)

Kode tersebutlah yang akan membuat Tabel tanpa Proteksi Keamanan untuk User. Pada Pesan di bawah ini, maksudnya adalah Tabel baru Tasks belum memiliki sistem pembatas akses user.

New table will not have Row Level Security enabled

Langsung saja, klik pada Tombol "Run and Enable RLS" untuk menjalankan Query SQL dan mengaktifkan RLS di Supabase.

Apa Risiko Jika Tanpa RLS?

Jika Tanpa Row Level Security (RLS), berarti Semua User bisa membaca/mengubah data Tasks melalui API Supabase jika menggunakan :

  • Anon Key
  • Authenticated Key

Contoh bahayanya, misalkan :

  • User A Login
  • User B Login

Jika Tanpa RLS, maka User B dapat melihat Task User A. Karena PostgreSQL belum diberi aturan pembatas user.

Jadi, Fitur RLS di dalam Supabase itu adalah untuk membatasi Akses per Baris Data (Row).

Namun, jika ingin membuat Tabel secara Manual, langsung saja klik pada bagian "Table Editor" pada Dashboard Project di Supabase.

Kemudian, klik "New Table" untuk membuat Tabel baru.

a. Tabel id

Isilah Tabel id tersebut dengan :

  • Name : id
  • Type : uuid
  • Default Value : uuid_generate_v4()
  • Primary Key✔️
  • Is Nullable :

Kolom ini menjadi Identitas unik Task yang dibuat otomatis saat insert data. 

b. Tabel user_id

Isilah Tabel user_id tersebut dengan :

  • Name : user_id
  • Type : uuid
  • Default Value : uuid_generate_v4()
  • Is Nullable : 

c. Tabel title

Isilah Tabel title tersebut dengan :

  • Name : title
  • Type : text
  • Is Nullable : 

d. Tabel created_at

Isilah Tabel created_at tersebut dengan :

  • Name : created_at
  • Typetimestamp
  • Default Value : now()

Inilah Hasil dari Semua Tabel/Kolom yang telah ditambahkan sebelumnya :

e. Menambahkan Foreign Key

Untuk menambahkan Foreign Key, kita butuh yang namanya salah satu Kolom saja, yaitu kolom user_id. Karena kolom tersebut digunakan untuk membuat hubungan (relationship) antara tabel tasks dan tabel lain, yaitu auth.users milik Supabase. Sebuah Foreign Key berfungsi sebagai referensi ke Primary Key pada tabel lain, dan dalam kasus ini user_id merujuk ke auth.users.id agar setiap task memiliki pemilik (owner) yang valid. Sementara itu, kolom lain seperti id, title, dan created_at tidak mereferensikan tabel lain: id adalah Primary Key untuk identitas unik task itu sendiri, title hanya menyimpan teks isi todo, dan created_at hanya menyimpan waktu pembuatan data. Karena hanya user_id yang membangun relasi antar tabel dan menjaga integritas data antar user dan task, maka hanya kolom itulah yang disebut Foreign Key.

Untuk kembali ke Pengaturannya, silakan klik 

Setelah itu, Klik "Add Foreign key Relation" pada Tabel user_id.

Langkah Pertama, dengan memilih Schema-nya menjadi auth.

Kedua, pilihlah Referensi Tabel untuk users pada bagian "Select a Table to Reference to".

Selanjutnya, pilihlah Referensi Kolom dari user_id ke id pada uuid.

Atau, inilah Rincian Tabel Relasi di atas agar lebih jelas :

  • Schema : auth
  • Table : users
  • Column : user_id → id

Terakhir, akan seperti ini tampilannya, dan klik "Save" untuk menyimpan semua Perubahan di atas.

Selanjutnya, klik lagi pada Tombol "Save" pada Menu Update Tabel user_id tersebut.

4. Membuat RLS Policy

Karena tadi kita sudah mengaktifkan RLS, selanjutnya kita juga harus membuat RLS Policy.

Pertama, klik pada bagian "Authentication" di Dashboard Project Supabase. Lalu, klik pada bagian "Policies".

Seperti inilah Tampilan Policies pada bagian Authentication di Supabase. Selanjutnya, kita harus membuat 4 (Empat) Policy terlebih dahulu di bagian Authentication di Supabase, yaitu INSERT, SELECT, UPDATE, dan DELETE.

Meskipun, kita perlu mengaktifkan RLS Policy di semua Tabel, namun untuk Project ini hanya mengaktifkan pada bagian Tabel tasks saja. Alasannya, karena tabel itulah yang menyimpan data utama milik user dan diakses langsung oleh aplikasi React melalui API Supabase. Sementara itu, tabel seperti auth.users sudah dikelola otomatis oleh sistem authentication Supabase sehingga biasanya tidak perlu kita buat policy manual sendiri. Jika nanti aplikasi berkembang dan memiliki tabel lain seperti profiles, projects, comments, notes, atau uploads, maka tabel-tabel tersebut juga sebaiknya menggunakan RLS agar setiap user hanya bisa mengakses datanya sendiri. Jadi, RLS bukan khusus untuk tasks, melainkan mekanisme keamanan umum PostgreSQL/Supabase untuk semua tabel yang datanya perlu dibatasi berdasarkan user atau role tertentu.

Sebelum memulai, klik terlebih dahulu pada Tombol "Create Policy" di bagian Tabel tasks.

a. Menambahkan Policy INSERT

Inilah Kode SQL untuk Policy INSERT :

create policy "Insert own todos"
on "ToDoList"
for insert
to authenticated
with check (auth.uid() = user_id);

Tujuannya untuk mengizinkan user login dan membuat todo miliknya sendiri.

b. Menambahkan Policy SELECT

Inilah Kode SQL untuk Policy SELECT :

create policy "Select own todos"
on "ToDoList"
for select
to authenticated
using (auth.uid() = user_id);

Tujuannya untuk mengizinkan user melihat task miliknya sendiri.

c. Menambahkan Policy UPDATE

Inilah Kode SQL untuk Policy UPDATE :

create policy "Update own todos"
on "ToDoList"
for update
to authenticated
using (auth.uid() = user_id);

Tujuannya untuk mengizinkan user mengedit task miliknya sendiri.

d. Menambahkan Policy DELETE

Inilah Kode SQL untuk Policy DELETE :

create policy "Delete own todos"
on "ToDoList"
for delete
to authenticated
using (auth.uid() = user_id);

Tujuannya untuk mengizinkan user menghapus task miliknya sendiri.

e. Kode Lengkapnya

Jika bingung dan ribet dengan cara di atas, inilah yang lebih praktisnya. Daripada menggunakan GUI di Menu Authentication --> Policies, mending Anda juga bisa pergi langsung ke SQL Editor.

Kode SQL hanya dari Keempat Policy saja :

create policy "Insert own tasks"
on tasks
for insert
to authenticated
with check (auth.uid() = user_id);

create policy "Select own tasks"
on tasks
for select
to authenticated
using (auth.uid() = user_id);

create policy "Update own tasks"
on tasks
for update
to authenticated
using (auth.uid() = user_id);

create policy "Delete own tasks"
on tasks
for delete
to authenticated
using (auth.uid() = user_id);

Inilah Kode SQL Lengkapnya jika ingin membuat Tabel-nya sekalian :

create extension if not exists pgcrypto;

create table "ToDoList" (
  id uuid primary key default gen_random_uuid(),
  name text not null,
  "isCompleted" boolean default false,
  user_id uuid references auth.users(id) on delete cascade
);

alter table "ToDoList"
enable row level security;

create policy "Insert own todos"
on "ToDoList"
for insert
to authenticated
with check (auth.uid() = user_id);

create policy "Select own todos"
on "ToDoList"
for select
to authenticated
using (auth.uid() = user_id);

create policy "Update own todos"
on "ToDoList"
for update
to authenticated
using (auth.uid() = user_id);

Caranya sama seperti diatas, tinggal klik saja Tombol "Run" untuk menjalankan Kueri SQL tersebut.

C. Membuat Proyek Aplikasi CRUD Sederhana (React + Vite)

Jika di atas, sudah melakukan Konfigurasi di Supabase, sekarang mari kita membuat File Kodingan Aplikasi CRUD To-do List Sederhana dengan React.js dan Vite di Supabase.

Untuk Struktur Folder-nya, sudah dijelaskan di atas.

1. File Utama (main.jsx)

File main.jsx adalah titik awal (entry point) aplikasi React yang dijalankan pertama kali oleh Vite. File ini bertugas untuk me-render komponen utama App.jsx ke dalam elemen HTML dengan id root yang berada di index.html. Secara sederhana, main.jsx adalah “jembatan” yang menghubungkan React dengan browser sehingga seluruh aplikasi dapat tampil di layar pengguna.

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

2. Root Component (App.jsx)

File App.jsx merupakan komponen utama (root component) yang mengatur alur keseluruhan aplikasi To-Do. Di dalam file ini dilakukan pengecekan apakah user sudah login atau belum menggunakan supabase.auth.getUser(). Jika user belum login, maka komponen Auth.jsx akan ditampilkan. Jika user sudah login, maka aplikasi akan menampilkan fitur utama seperti form tambah task (AddTodo.jsx) dan daftar task (TodoList.jsx). Dengan kata lain, App.jsx berfungsi sebagai pusat kontrol aplikasi dan pengatur autentikasi user.

import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
import Auth from './components/Auth'
import TodoList from './components/TodoList'
import AddTodo from './components/AddTodo'
import { LogOut } from 'lucide-react'

function App() {
  const [session, setSession] = useState(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session)
      setLoading(false)
    })

    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session)
    })
  }, [])

  const handleLogout = async () => {
    await supabase.auth.signOut()
  }

  if (loading) {
    return (
      <div className="app-container">
        <p className="empty-state">Loading...</p>
      </div>
    )
  }

  return (
    <div className="app-container">
      {!session ? (
        <Auth />
      ) : (
        <>
          <div className="header-container">
            <h1>My Tasks</h1>
            <button className="logout-btn" onClick={handleLogout} title="Log out">
              <LogOut size={18} />
            </button>
          </div>
          <AddTodo session={session} />
          <TodoList session={session} />
        </>
      )}
    </div>
  )
}

export default App

3. Todo List (TodoList.jsx)

Selanjutnya, kita akan membuat File Todo List yang terletak di Folder src/components/TodoList.jsx.

File TodoList.jsx bertugas mengambil dan menampilkan seluruh task milik user yang sedang login. Data diambil dari tabel tasks menggunakan query Supabase seperti .select('*').eq('user_id', user.id) sehingga hanya task milik user tersebut yang ditampilkan. File ini juga memiliki fitur delete task menggunakan .delete().eq('id', id). Selain itu, komponen menggunakan useEffect() agar data otomatis diambil saat komponen pertama kali dimuat. Singkatnya, file ini adalah bagian utama yang menampilkan isi To-Do List kepada pengguna.

import { useState, useEffect } from 'react'
import { supabase } from '../supabaseClient'
import { Trash2 } from 'lucide-react'

export default function TodoList({ session }) {
  const [todos, setTodos] = useState([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    fetchTodos()

    // Subscribe to changes
    const channel = supabase
      .channel('tasks_changes')
      .on(
        'postgres_changes',
        {
          event: '*',
          schema: 'public',
          table: 'tasks',
          filter: `user_id=eq.${session.user.id}`,
        },
        () => {
          fetchTodos()
        }
      )
      .subscribe()

    return () => {
      supabase.removeChannel(channel)
    }
  }, [session.user.id])

  const fetchTodos = async () => {
    try {
      const { data, error } = await supabase
        .from('tasks')
        .select('*')
        .order('created_at', { ascending: false })

      if (error) throw error
      setTodos(data)
    } catch (error) {
      console.error('Error fetching tasks:', error)
    } finally {
      setLoading(false)
    }
  }

  const deleteTodo = async (id) => {
    try {
      const { error } = await supabase.from('tasks').delete().eq('id', id)
      if (error) throw error
    } catch (error) {
      console.error('Error deleting task:', error)
      alert('Failed to delete task')
    }
  }

  if (loading) {
    return <div className="empty-state">Loading tasks...</div>
  }

  if (todos.length === 0) {
    return <div className="empty-state">No tasks yet. Add one above!</div>
  }

  return (
    <ul className="todo-list">
      {todos.map((todo) => (
        <li key={todo.id} className="todo-item">
          <div className="todo-content">
            <span className="todo-title">{todo.title}</span>
            <span className="todo-date">
              {new Date(todo.created_at).toLocaleDateString()}{' '}
              {new Date(todo.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
            </span>
          </div>
          <button
            className="danger"
            onClick={() => deleteTodo(todo.id)}
            title="Delete task"
          >
            <Trash2 size={18} />
          </button>
        </li>
      ))}
    </ul>
  )
}

4. Add Todo (AddTodo.jsx)

Selanjutnya, kita akan membuat File Add Todo yang terletak di Folder src/components/AddTodo.jsx.

File AddTodo.jsx digunakan untuk menambahkan task/todo baru ke database PostgreSQL di Supabase. Komponen ini memiliki Input Text untuk menulis task dan tombol Add untuk menyimpan data. Saat user menekan tombol Add, fungsi supabase.from('tasks').insert() akan dijalankan untuk memasukkan data baru ke tabel tasks, termasuk user_id dari user yang sedang login agar task memiliki pemilik yang jelas. Setelah data berhasil ditambahkan, input akan dikosongkan dan daftar task akan diperbarui.

import { useState } from 'react'
import { supabase } from '../supabaseClient'
import { Plus } from 'lucide-react'

export default function AddTodo({ session }) {
  const [title, setTitle] = useState('')
  const [loading, setLoading] = useState(false)

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (!title.trim()) return

    setLoading(true)
    const { error } = await supabase.from('tasks').insert([
      { title: title.trim(), user_id: session.user.id }
    ])

    if (error) {
      console.error('Error adding task:', error)
      alert(error.message)
    } else {
      setTitle('')
    }
    setLoading(false)
  }

  return (
    <form className="todo-form" onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="What needs to be done?"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        disabled={loading}
      />
      <button type="submit" disabled={loading || !title.trim()}>
        <Plus size={18} />
        Add
      </button>
    </form>
  )
}

5. Auth Component (Auth.jsx)

Kemudian, kita juga membuat File Auth Component yang terletak di Folder src/components/Auth.jsx.

File Auth.jsx bertanggung jawab untuk proses autentikasi/login user. Pada proyek ini, login menggunakan metode Magic Link dari Supabase Auth, yaitu user hanya memasukkan email tanpa password, lalu Supabase akan mengirim link login ke email tersebut. File ini menggunakan React Hook useState untuk menyimpan input email sementara, kemudian menjalankan supabase.auth.signInWithOtp() saat tombol login ditekan. Komponen ini menjadi gerbang utama sebelum user dapat mengakses fitur To-Do App.

import { useState } from 'react'
import { supabase } from '../supabaseClient'
import { Mail } from 'lucide-react'

export default function Auth() {
  const [loading, setLoading] = useState(false)
  const [email, setEmail] = useState('')
  const [message, setMessage] = useState('')

  const handleLogin = async (event) => {
    event.preventDefault()
    setLoading(true)
    setMessage('')
    const { error } = await supabase.auth.signInWithOtp({
      email,
    })

    if (error) {
      alert(error.error_description || error.message)
    } else {
      setMessage('Check your email for the login link!')
    }
    setLoading(false)
  }

  return (
    <div>
      <h1>TaskFlow</h1>
      <p className="subtitle">Sign in via magic link with your email below</p>
     
      {message ? (
        <div className="auth-message">{message}</div>
      ) : (
        <form className="auth-container" onSubmit={handleLogin}>
          <input
            type="email"
            placeholder="Your email address"
            value={email}
            required
            onChange={(e) => setEmail(e.target.value)}
          />
          <button type="submit" disabled={loading || !email}>
            <Mail size={18} />
            {loading ? 'Sending...' : 'Send Magic Link'}
          </button>
        </form>
      )}
    </div>
  )
}

6. File CSS (index.css)

Terakhir, kita juga membuat File CSS yang terletak di Folder src/index.css.

File index.css digunakan untuk mengatur tampilan visual aplikasi seperti warna, margin, padding, font, layout, button styling, dan responsive design. Walaupun tidak berpengaruh langsung pada logic aplikasi, file ini sangat penting untuk meningkatkan user experience (UX) agar aplikasi terlihat lebih modern, rapi, dan nyaman digunakan.

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

:root {
  --bg-color: #0f172a;
  --surface-color: #1e293b;
  --primary-color: #3b82f6;
  --primary-hover: #2563eb;
  --text-color: #f8fafc;
  --text-muted: #94a3b8;
  --border-color: #334155;
  --danger-color: #ef4444;
  --danger-hover: #dc2828;
  --success-color: #10b981;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Inter', sans-serif;
  background-color: var(--bg-color);
  color: var(--text-color);
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.app-container {
  width: 100%;
  max-width: 600px;
  min-height: 80vh;
  padding: 2rem;
  background: var(--surface-color);
  border-radius: 1rem;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.3);
  border: 1px solid var(--border-color);
}

h1 {
  text-align: center;
  margin-bottom: 2rem;
  font-size: 2.5rem;
  font-weight: 700;
  background: linear-gradient(to right, #60a5fa, #3b82f6);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

p.subtitle {
  text-align: center;
  color: var(--text-muted);
  margin-bottom: 2rem;
}

button {
  background-color: var(--primary-color);
  color: white;
  border: none;
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  font-size: 1rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

button:hover:not(:disabled) {
  background-color: var(--primary-hover);
  transform: translateY(-1px);
}

button:active:not(:disabled) {
  transform: translateY(0);
}

button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

button.danger {
  background-color: transparent;
  color: var(--danger-color);
  padding: 0.5rem;
}

button.danger:hover:not(:disabled) {
  background-color: rgba(239, 68, 68, 0.1);
  color: var(--danger-hover);
}

input {
  width: 100%;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  border: 1px solid var(--border-color);
  background-color: rgba(15, 23, 42, 0.5);
  color: var(--text-color);
  font-size: 1rem;
  transition: border-color 0.2s;
}

input:focus {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}

/* Auth Container */
.auth-container {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 400px;
  margin: 0 auto;
}

.auth-message {
  text-align: center;
  padding: 1rem;
  background-color: rgba(16, 185, 129, 0.1);
  color: var(--success-color);
  border-radius: 0.5rem;
  border: 1px solid rgba(16, 185, 129, 0.2);
}

/* Todo List */
.todo-form {
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
}

.todo-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.todo-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  background-color: rgba(15, 23, 42, 0.4);
  border-radius: 0.5rem;
  border: 1px solid var(--border-color);
  transition: all 0.2s ease;
}

.todo-item:hover {
  border-color: var(--text-muted);
  transform: translateX(2px);
}

.todo-content {
  display: flex;
  flex-direction: column;
}

.todo-title {
  font-weight: 500;
}

.todo-date {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 0.25rem;
}

.header-container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
}

.header-container h1 {
  margin-bottom: 0;
  font-size: 2rem;
}

.logout-btn {
  background-color: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border-color);
  padding: 0.5rem 1rem;
}

.logout-btn:hover:not(:disabled) {
  background-color: var(--border-color);
  color: var(--text-color);
}

.empty-state {
  text-align: center;
  color: var(--text-muted);
  padding: 2rem;
  font-style: italic;
}





D. Demo Aplikasi

1. Menjalankan Aplikasi

Jika sudah selesai, maka langsung saja ketik Perintah ini di Command Line :

npm run dev

Kemudian, seperti inilah Tampilan dari Aplikasi yang telah dibuat di atas. Masukkan Email sembarang saja tapi dengan Email yang sama dengan waktu pendaftaran Email di Akun Supabase. Setelah itu klik "Send Magic Link" untuk mengirimnya.

Tunggulah sampai proses pengiriman selesai. Biasanya, Periksa Email yang masuk.

A

A

A

A

A

A

2. Memeriksa Database

Jika kita lihat kembali di Table Editor di Supabase, maka Database-nya sudah terisi seperti ini :

A

A

A




















VIDEO

Untuk melihat Tutorial lainnya dan lebih jelasnya tentang Tutorial Supabase, silakan lihat Video-video YouTube di bawah ini.

1. Pengertian Supabase

2. Cara Menggunakan Supabase


Itulah Penjelasan dan Tutorial Cara Membuat Database dengan menggunakan Supabase. Untuk melihat Artikel sebelumnya mengenai Perbandingan antara Firebase dan Supabase, silakan lihat di sini.

Atau, jika ingin melihat Tutorial Pemrograman sebelumnya tentang Aplikasi REST API PHP Sederhana, silakan lihat di sini.

Terima Kasih 😄😘👌👍 :)

Wassalamu‘alaikum wr. wb.

Post a Comment

Previous Post Next Post