Supabase #
Dalam siklus pengembangan aplikasi perangkat lunak modern, kecepatan rilis ke pasar (time-to-market) sering kali menjadi pembeda utama antara kesuksesan dan kegagalan sebuah produk. Namun, membangun sistem backend yang tangguh dari awal bukanlah pekerjaan mudah. Tim pengembang harus merancang skema database, membangun server API (REST atau GraphQL), mengimplementasikan sistem autentikasi dan otorisasi, mengelola penyimpanan file statis, serta mengonfigurasi mekanisme sinkronisasi data secara real-time. Proses berulang (boilerplate) ini memakan waktu berminggu-minggu bahkan berbulan-bulan sebelum tim dapat mulai menulis baris kode pertama untuk fitur bisnis utama mereka.
Untuk mengatasi kerumitan tersebut, konsep Backend-as-a-Service (BaaS) lahir. Google Firebase menjadi pelopor utama yang menawarkan backend terkelola instan, namun ia memiliki keterbatasan mendasar: Firebase menggunakan database NoSQL berpemilik (proprietary) yang menyulitkan kueri relasional kompleks, memicu risiko keterikatan vendor (vendor lock-in), serta sulit untuk di-host secara mandiri jika diperlukan. Kesenjangan inilah yang coba dijembatani oleh Supabase. Supabase hadir sebagai alternatif open-source dari Firebase yang dibangun di atas PostgreSQL, database relasional paling stabil, kaya fitur, dan tepercaya di dunia.
Supabase merangkum kekuatan PostgreSQL ke dalam platform terpadu yang mengekspos API siap pakai secara otomatis. Dengan memadukan database relasional yang kuat dengan utilitas modern seperti autentikasi instan, manajemen penyimpanan berkas terintegrasi, fungsi komputasi tanpa server (serverless edge functions), serta penyiaran data real-time, Supabase menawarkan solusi backend kelas produksi tanpa mengorbankan integritas data transaksional relasional. Artikel ini akan membedah secara mendalam arsitektur internal Supabase, mekanisme keamanan Row-Level Security (RLS), cara kerja penyiaran event real-time, serta implementasi praktisnya di tingkat produksi.
Apa Itu Supabase? Membedah Komponen Utama Platform #
Supabase bukanlah satu aplikasi monolitik tunggal, melainkan sebuah integrasi dari beberapa alat open-source berskala industri yang dikustomisasi dengan erat di sekitar PostgreSQL. Supabase bertindak sebagai lapisan middleware cerdas yang membungkus database PostgreSQL sehingga dapat diakses secara langsung dari aplikasi klien (frontend/mobile) dengan aman.
Komponen-komponen penyusun arsitektur Supabase meliputi:
1. Database (PostgreSQL) #
Ini adalah jantung dari seluruh layanan Supabase. Supabase tidak menyembunyikan database di balik abstraksi terbatas; kita mendapatkan akses PostgreSQL penuh dengan hak akses superuser. Kita dapat menulis SQL mentah, membuat tabel relasional, mendefinisikan foreign keys, menulis fungsi tersimpan (stored procedures), memicu triggers, hingga memasang ekstensi PostgreSQL populer seperti pgvector untuk pencarian kemiripan AI vector, PostGIS untuk data geografis, atau pgcrypto untuk kriptografi.
2. PostgREST (Penyusun API REST Instan) #
PostgREST adalah web server mandiri yang secara otomatis menerjemahkan struktur tabel, relasi, dan fungsi di dalam PostgreSQL menjadi RESTful API yang bersih secara langsung. Setiap kali kita membuat tabel baru di database, PostgREST langsung menyediakan endpoint HTTP untuk operasi CRUD (Create, Read, Update, Delete) lengkap dengan kemampuan penyaringan (filtering), pengurutan (sorting), serta pembagian halaman (pagination) tanpa kita perlu menulis satu pun baris kode backend.
3. GoTrue (Layanan Autentikasi) #
GoTrue adalah API berbasis Go yang mengelola pendaftaran pengguna, login, verifikasi email, pemulihan kata sandi, serta integrasi dengan penyedia OAuth eksternal (seperti Google, GitHub, Apple, dll.). GoTrue mengeluarkan token keamanan standar industri berupa JSON Web Token (JWT) yang kemudian dikirimkan oleh klien ke database PostgreSQL untuk memvalidasi hak akses data.
4. Realtime (Sinkronisasi Data berbasis Elixir) #
Komponen Realtime adalah server yang ditulis dalam bahasa pemrograman Elixir (menggunakan framework Phoenix) untuk mendukung ribuan koneksi WebSocket secara simultan dengan latensi rendah. Layanan ini mendengarkan perubahan data langsung dari Write-Ahead Log (WAL) PostgreSQL, menyaring perubahan tersebut berdasarkan izin keamanan pengguna, dan menyiarkannya ke klien yang berlangganan secara real-time.
5. Storage (Manajemen Berkas Terintegrasi) #
Layanan Storage menyediakan API untuk mengunggah, mengunduh, dan mengelola berkas statis (seperti gambar, dokumen, atau video). Di bawah kap, Storage menyimpan berkas fisik di penyimpanan objek awan (seperti AWS S3) dan mencatat metadata file ke dalam tabel database PostgreSQL sehingga dapat dikontrol aksesnya menggunakan kebijakan keamanan database biasa.
6. Edge Functions (Deno Runtime) #
Ketika kita membutuhkan logika bisnis kustom yang tidak boleh berjalan di sisi klien (seperti integrasi pembayaran Stripe, verifikasi webhook eksternal, atau pemrosesan data sensitif), Supabase menyediakan Edge Functions. Ini adalah fungsi serverless stateless yang berjalan di atas runtime Deno yang sangat cepat dan tersebar secara global di dekat lokasi fisik pengguna akhir.
Arsitektur Hubungan Klien dan Layer Supabase #
Untuk memahami bagaimana request dari aplikasi pengguna diproses hingga ke database PostgreSQL, kita perlu melihat diagram alur arsitektur platform Supabase berikut:
flowchart TD
subgraph ClientEnvironment["Antarmuka Klien"]
ClientApp["Aplikasi Klien (React/Flutter/Vue)"]
end
subgraph SupabasePlatform["Stack Terkelola Supabase"]
direction TB
Kong["Kong API Gateway (Routing & Rate Limiting)"]
GoTrue["GoTrue (Auth Service)"]
PostgREST["PostgREST (REST API Compiler)"]
Realtime["Realtime Service (Elixir Node)"]
DenoEdge["Deno Edge Runtime (Edge Functions)"]
Storage["Storage API (File Manager)"]
subgraph DBCluster["Database Layer"]
Postgres["PostgreSQL Database"]
RLS["Row-Level Security (RLS) Engine"]
end
end
ClientApp -->|"HTTPS / WSS Request"| Kong
Kong -->|"Rute Auth"| GoTrue
Kong -->|"Rute REST"| PostgREST
Kong -->|"Rute Websocket"| Realtime
Kong -->|"Rute Functions"| DenoEdge
Kong -->|"Rute File Upload"| Storage
GoTrue -->|"Verifikasi JWT & Baca Claims"| Postgres
PostgREST -->|"Hasilkan SQL & Query"| Postgres
Realtime -. "Listen to WAL Logs" .-> Postgres
Storage -->|"Penyimpanan Metadata"| Postgres
DenoEdge -->|"Eksekusi Query"| Postgres
Postgres --> RLS
style Postgres stroke:#0288d1,stroke-width:2px
style Kong stroke:#0288d1,stroke-width:2px
Kong bertindak sebagai gerbang pintu masuk tunggal (API Gateway) yang menerima koneksi dari luar. Kong bertugas melakukan otorisasi awal, melakukan pembatasan laju kueri (rate limiting) untuk melindungi sistem dari serangan DDoS, serta merutekan request HTTP/WebSocket ke layanan mikro internal yang sesuai.
Keamanan Data Tanpa Middleware: Row-Level Security (RLS) #
Keunikan arsitektur Supabase yang mengizinkan aplikasi klien terhubung langsung ke database tanpa lapisan server perantara melahirkan pertanyaan kritis: bagaimana kita mengamankan data agar pengguna tidak dapat membaca atau memodifikasi data milik pengguna lain?
Jawaban dari tantangan keamanan ini adalah Row-Level Security (RLS), sebuah fitur keamanan bawaan mesin PostgreSQL.
Di database relasional tradisional, izin akses data biasanya dikontrol di tingkat tabel (table-level); pengguna memiliki izin untuk membaca seluruh isi tabel atau tidak sama sekali. Dengan RLS, PostgreSQL mengevaluasi hak akses untuk setiap baris data secara individual berdasarkan aturan kebijakan (policies) yang kita definisikan.
Ketika seorang pengguna melakukan login melalui layanan autentikasi Supabase, mereka menerima sebuah token JWT. Token ini berisi informasi identitas pengguna (claims) seperti ID unik pengguna (sub/uid) dan peran akses (role). Setiap kali klien mengirimkan kueri SQL melalui PostgREST, token JWT tersebut disuntikkan ke dalam parameter sesi PostgreSQL.
Di dalam database, kita dapat menulis aturan RLS menggunakan fungsi bawaan Supabase auth.uid() untuk mencocokkan ID pengguna di token JWT dengan kolom pemilik di baris tabel:
-- Mengaktifkan Row-Level Security pada tabel orders
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Membuat kebijakan agar pengguna hanya bisa membaca pesanan mereka sendiri
CREATE POLICY "Pengguna hanya dapat melihat order mereka sendiri"
ON orders FOR SELECT
TO authenticated
USING (auth.uid() = user_id);
Ketika kueri berjalan, mesin PostgreSQL secara otomatis memodifikasi kueri SQL masuk dengan menambahkan klausa WHERE auth.uid() = user_id di balik layar. Dengan demikian, tidak ada celah bagi pengguna untuk menerobos data milik orang lain, bahkan jika mereka mencoba memodifikasi parameter kueri di sisi frontend.
Real-time Engine: Mendengarkan Log WAL PostgreSQL #
Fitur real-time adalah salah satu daya tarik utama Supabase yang memungkinkan aplikasi menerima pembaruan instan saat terjadi perubahan data. Supabase mencapai ini tanpa menggunakan database real-time khusus seperti Firebase Realtime Database. Sebagai gantinya, Supabase mendengarkan Write-Ahead Log (WAL) PostgreSQL.
WAL adalah log persisten tempat PostgreSQL mencatat semua transaksi penulisan (INSERT, UPDATE, DELETE) sebelum perubahan tersebut dituliskan secara fisik ke blok penyimpanan disk. Ini digunakan PostgreSQL untuk keperluan pemulihan data (crash recovery) dan replikasi data.
Berikut alur kerja bagaimana penyiaran real-time berlangsung:
- Aplikasi klien membuka koneksi WebSocket persisten menuju layanan Realtime Supabase dan mendaftarkan minat berlangganan pada tabel tertentu (misal:
messages). - Klien atau admin melakukan penulisan data ke tabel
messages. - Mesin PostgreSQL menuliskan log kejadian transaksi tersebut ke WAL.
- Komponen Realtime (Elixir) yang terhubung sebagai klien replikasi logis PostgreSQL menerima aliran data WAL tersebut secara instan.
- Sebelum menyiarkan data tersebut ke WebSocket klien, server Realtime mengevaluasi kebijakan RLS pada database. Hal ini memastikan bahwa data perubahan hanya dikirimkan ke klien yang memang memiliki hak akses sah untuk melihat baris data tersebut. Jika RLS menolak akses, event tidak akan disiarkan ke klien tersebut.
- Klien menerima payload perubahan data di aplikasi mereka dan memperbarui antarmuka pengguna secara instan.
Edge Functions: Runtime Deno Serverless #
Meskipun sebagian besar logika aplikasi dapat ditangani langsung oleh kombinasi API otomatis dan RLS, selalu ada skenario di mana kita membutuhkan eksekusi kode backend kustom yang aman (secure backend logic). Supabase menyediakan Edge Functions untuk kebutuhan ini.
Supabase memilih runtime Deno alih-alih Node.js untuk Edge Functions karena Deno menawarkan waktu start-up yang sangat cepat (near-zero cold starts), keamanan bawaan yang ketat (akses ke disk, jaringan, dan environment harus diizinkan secara eksplisit), serta dukungan asli terhadap TypeScript tanpa perlu transpiler eksternal.
Beberapa skenario ideal penggunaan Edge Functions:
- Integrasi Stripe: Menerima webhook pembayaran dari Stripe, memvalidasi tanda tangan kriptografi, dan memperbarui status langganan pengguna di database.
- Pengiriman Email & SMS: Menghubungkan aplikasi ke layanan pihak ketiga seperti Twilio, Resend, atau SendGrid untuk mengirim pesan transaksional.
- Pemrosesan AI: Mengirimkan teks ke API OpenAI untuk menghasilkan embedding, lalu menyimpannya ke kolom bertipe vector di PostgreSQL.
Penerapan Praktis Kode: Autentikasi dan CRUD dengan RLS di TypeScript #
Mari kita terapkan pembuatan sistem aplikasi to-do list sederhana menggunakan Supabase dengan skema database relasional lengkap dengan konfigurasi keamanan RLS, diikuti oleh penulisan kode JavaScript/TypeScript klien menggunakan SDK @supabase/supabase-js.
1. Script Migrasi SQL Database #
Pertama, kita jalankan script SQL berikut di SQL Editor konsol Supabase kita untuk membuat tabel, mengaktifkan RLS, dan mendefinisikan kebijakan keamanan.
-- Buat tabel tugas (todos)
CREATE TABLE todos (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
title TEXT NOT NULL,
is_completed BOOLEAN DEFAULT FALSE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL
);
-- Aktifkan RLS pada tabel todos
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
-- Kebijakan 1: Pengguna terautentikasi dapat membaca tugas mereka sendiri
CREATE POLICY "Pengguna dapat membaca tugas mereka"
ON todos FOR SELECT
TO authenticated
USING (auth.uid() = user_id);
-- Kebijakan 2: Pengguna terautentikasi dapat membuat tugas baru atas nama mereka sendiri
CREATE POLICY "Pengguna dapat membuat tugas baru"
ON todos FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = user_id);
-- Kebijakan 3: Pengguna terautentikasi dapat memperbarui tugas milik mereka sendiri
CREATE POLICY "Pengguna dapat memperbarui tugas"
ON todos FOR UPDATE
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Kebijakan 4: Pengguna terautentikasi dapat menghapus tugas milik mereka sendiri
CREATE POLICY "Pengguna dapat menghapus tugas"
ON todos FOR DELETE
TO authenticated
USING (auth.uid() = user_id);
2. Implementasi Klien TypeScript #
Berikut adalah kode program di sisi frontend untuk menghubungkan aplikasi ke Supabase, melakukan proses registrasi pengguna, dan menjalankan operasi CRUD data dengan aman.
import { createClient } from '@supabase/supabase-js';
// Tipe data representasi tabel todos
export interface Todo {
id?: number;
user_id?: string;
title: string;
is_completed: boolean;
created_at?: string;
}
// Inisialisasi Klien Supabase
const supabaseUrl = 'https://your-project-ref.supabase.co';
// JANGAN SEKALI-KALI mengekspos service_role_key di kode frontend!
// Gunakan anon_key yang aman karena akses akan dibatasi secara ketat oleh RLS.
const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Fungsi mendaftarkan pengguna baru
export async function signUpUser(email: string, password: string) {
// ✓ BENAR: Mendaftar menggunakan GoTrue auth API
const { data, error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
console.error('Gagal melakukan pendaftaran:', error.message);
throw error;
}
return data.user;
}
// Fungsi masuk ke aplikasi (Sign In)
export async function signInUser(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
console.error('Gagal masuk log:', error.message);
throw error;
}
return data.session;
}
// Fungsi mengambil daftar todos (Tugas)
export async function fetchMyTodos(): Promise<Todo[]> {
// PostgREST secara otomatis mengevaluasi token JWT session saat ini
// dan PostgreSQL menerapkan filter RLS auth.uid() = user_id di bawah kap.
const { data, error } = await supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false });
if (error) {
console.error('Gagal mengambil data todos:', error.message);
throw error;
}
return data as Todo[];
}
// Fungsi membuat todo baru
export async function createTodo(title: string): Promise<Todo> {
// Dapatkan user ID aktif saat ini dari session
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
throw new Error('Pengguna tidak terautentikasi');
}
const newTodo: Todo = {
title,
is_completed: false,
user_id: user.id // Wajib sama dengan auth.uid() agar lolos kebijakan CHECK RLS
};
const { data, error } = await supabase
.from('todos')
.insert([newTodo])
.select()
.single();
if (error) {
console.error('Gagal menyimpan todo baru:', error.message);
throw error;
}
return data as Todo;
}
// Fungsi memperbarui status kelengkapan todo
export async function toggleTodoCompletion(id: number, isCompleted: boolean): Promise<Todo> {
const { data, error } = await supabase
.from('todos')
.update({ is_completed: isCompleted })
.eq('id', id)
.select()
.single();
if (error) {
console.error('Gagal memperbarui status todo:', error.message);
throw error;
}
return data as Todo;
}
Kelebihan, Kekurangan, dan Kapan Beralih ke Database Mandiri #
Meskipun Supabase menawarkan produktivitas yang luar biasa tinggi untuk pembangunan aplikasi, ia bukanlah peluru perak (silver bullet) yang cocok untuk semua skenario arsitektur perangkat lunak.
TETAP GUNAKAN SUPABASE jika:
✓ Ingin membangun aplikasi web/mobile dengan cepat menggunakan basis data SQL.
✓ Membutuhkan fitur otentikasi pengguna, penyimpanan file, dan API instan tanpa menulis server API.
✓ Lebih menyukai kepatuhan standar open-source untuk menghindari keterikatan vendor (vendor lock-in).
✓ Memiliki data yang membutuhkan relasi kompleks dan jaminan integritas data ACID.
PERTIMBANGKAN ALTERNATIF lain jika:
✗ Logika bisnis aplikasi sangat kompleks dan membutuhkan banyak transaksi multi-tabel terdistribusi.
✗ Beban kueri database sangat analitikal (OLAP) skala besar (gunakan data warehouse).
✗ Tidak bersahabat dengan konsep penulisan aturan keamanan di tingkat database (RLS SQL).
Tabel Evaluasi BaaS Modern #
| Parameter | Supabase Platform | Firebase (Google) | PostgreSQL Mandiri + Custom Backend |
|---|---|---|---|
| Mesin Database | PostgreSQL (Relasional) | Firestore & Realtime DB (NoSQL) | Bebas (Postgres, MySQL, dll) |
| Penyusunan API | Otomatis (REST & GraphQL) | SDK Kustom Firebase | Manual (Express, Go, FastAPI) |
| Model Keamanan | SQL Row-Level Security (RLS) | Firebase Security Rules | Logika Middleware Server |
| Fitur Real-time | Aliran WAL (Logical Replication) | WebSocket Listener Native | Manual via WebSockets / PubSub |
| Kemudahan Migrasi | Sangat Mudah (Standard SQL Dump) | Sangat Sulit (Ekspor JSON Khusus) | Sangat Mudah |
| Model Biaya | Berbasis Resource (VM & Disk) | Berbasis Operasi Baca/Tulis | Berbasis VM Instance |
Ringkasan #
- Supabase adalah BaaS open-source yang membungkus database PostgreSQL dengan antarmuka API REST/GraphQL otomatis, autentikasi, dan penyimpanan.
- Row-Level Security (RLS) bertindak sebagai benteng pertahanan utama, mengevaluasi hak akses setiap baris data langsung di dalam database berdasarkan JWT pengguna.
- Realtime Service yang ditulis di Elixir memantau data transaksi dari Write-Ahead Log (WAL) PostgreSQL untuk menyiarkan perubahan melalui WebSocket.
- Deno-based Edge Functions digunakan untuk menjalankan logika backend khusus yang membutuhkan lingkungan terisolasi, aman, dan berlatensi rendah.
- Transparansi Open-source memungkinkan kita mengekspor data secara penuh menggunakan command
pg_dumpstandar tanpa risiko vendor lock-in.- Integrasi dengan PostgreSQL membuka akses ke ekosistem ekstensi yang kaya seperti
pgvectoruntuk pemrosesan kecerdasan buatan (AI).