CRUD dengan Supabase JS Client
Saatnya 'ngobrol' dengan database Supabase dari kodemu! Pelajari cara melakukan operasi CRUD (Create, Read, Update, Delete) menggunakan Supabase JS Client (`@supabase/supabase-js`) dengan contoh query yang jelas.
"Ngobrol" Langsung Sama Database Supabase: Jurus CRUD Pake Supabase JS Client!
Udah punya tabel kue
yang cantik di database Supabase-mu? Udah juga nyiapin "saluran telepon" Supabase Client di proyek Next.js kita? Mantap! Sekarang, saatnya kita bener-bener "ngobrol" sama database itu dari kode JavaScript/TypeScript kita.
Kita bakal belajar cara ngelakuin empat operasi paling dasar yang jadi tulang punggung interaksi data: CRUD (Create, Read, Update, Delete). Dan kita bakal pake metode-metode keren dari Supabase JS Client (@supabase/supabase-js
) yang bikin semua ini jadi gampang dan intuitif.
Ingat:
- Pastikan kamu udah ngimpor dan nginisialisasi Supabase Client-mu (biasanya dari file kayak
src/lib/supabase-client.ts.ts
):typescript
// Di file tempat kamu mau query, misalnya di API Route atau Client Component import { supabase } from '@/lib/supabase-client.ts'; // Sesuaikan path-nya // Untuk contoh di bawah, kita asumsikan 'supabase' sudah jadi instance SupabaseClient.
- Semua operasi database pake Supabase Client itu asinkron (asynchronous), jadi kita bakal sering pake
async/await
. - Hasil dari query Supabase Client biasanya adalah objek yang punya dua properti utama:
data
(isinya hasil query kalau sukses) danerror
(isinya objek error kalau ada masalah). Selalu cekerror
-nya ya!
1. Create (Membuat Data Baru / insert
)
Buat nambahin baris data baru ke tabel kue
kita, kita pake metode .insert()
.
- Sintaks Dasar:
await supabase.from('nama_tabel').insert([{ ...objekDataBaru }, { ...objekDataLain }]);
from('nama_tabel')
: Nunjuk ke tabel mana kita mau insert.insert([...])
: Nerima array dari objek yang mau diinsert. Meskipun cuma satu objek, tetep harus di dalem array. Tiap objek strukturnya harus cocok sama kolom di tabelmu.
Contoh Membuat Kue Baru:
// Asumsikan ini di dalam sebuah fungsi async, misal di API Route Next.js
// atau di Client Component yang manggil Server Action.
// Impor tipe Kue jika perlu untuk type safety objek dataKueBaru
import { Kue } from '@/data/kue'; // Atau tipe yang sesuai dengan tabelmu
// Kita definisikan tipe untuk data input, tanpa id, created_at, updated_at
type KueInput = Omit<Kue, 'id' | 'created_at' | 'updated_at'>;
async function tambahKueBaru(dataKueBaru: KueInput) {
try {
const { data, error } = await supabase
.from('kue') // Nama tabel kita
.insert([
// Kita bisa insert satu atau beberapa objek sekaligus
{
nama: dataKueBaru.nama,
deskripsi_singkat: dataKueBaru.deskripsi_singkat,
deskripsi_lengkap: dataKueBaru.deskripsi_lengkap,
harga: dataKueBaru.harga,
gambar_url: dataKueBaru.gambar_url, // Pastikan nama kolom di DB sama
kategori: dataKueBaru.kategori,
rating: dataKueBaru.rating,
bahan_utama: dataKueBaru.bahan_utama
// id, created_at, updated_at biasanya otomatis diisi database
}
])
.select() // PENTING: .select() di akhir biar ngembaliin data yang baru diinsert
.single(); // Jika kita yakin hanya satu item yang diinsert dan ingin objeknya langsung
if (error) {
console.error("Error Supabase pas nambah kue:", error.message);
throw error; // Lempar error biar bisa ditangkep di atas
}
console.log("Kue baru berhasil ditambah:", data);
return data; // data di sini adalah objek kue yang baru ditambah (karena .single())
} catch (err: any) {
console.error("Gagal total nambah kue:", err.message);
return null;
}
}
// Contoh pemanggilan:
// const kueDonat: KueInput = {
// nama: "Donat Coklat Keju Spesial",
// deskripsi_singkat: "Donat empuk dengan topping coklat dan keju melimpah.",
// deskripsi_lengkap: "Donat klasik yang selalu jadi favorit...",
// harga: 8000,
// gambar_url: "/images/kue/donat-coklat-keju.jpg", // Nanti ganti dengan URL dari Supabase Storage
// kategori: "Donat",
// rating: 4.7,
// bahan_utama: ["Tepung", "Coklat", "Keju"]
// };
// tambahKueBaru(kueDonat);
.select().single()
setelahinsert()
: Kalau kamu mau dapet balik data yang baru aja diinsert (lengkap samaid
dancreated_at
yang di-generate database) sebagai satu objek (bukan array), tambahin.select().single()
di akhir.
2. Read (Membaca/Mengambil Data / select
)
Ini operasi yang paling sering dilakuin. Buat ngambil data, kita pake metode .select()
.
- Sintaks Dasar:
await supabase.from('nama_tabel').select('kolom1, kolom2, relasi(kolom_relasi)');
select('string_kolom_atau_query')
: String ini nentuin kolom apa aja yang mau diambil.'*'
: Ambil semua kolom.nama_kolom
: Ambil kolom spesifik.kolom1, kolom2
: Ambil beberapa kolom spesifik, dipisah koma.nama_tabel_relasi(kolom_di_relasi)
: Buat ngambil data dari tabel yang berelasi (join). (Ini lebih advance, kita fokus ke kolom tabel utama dulu).
- Kamu bisa nambahin berbagai modifier (filter, urutan, limit) sebelum
.select()
.
Contoh Mengambil Data Kue:
// Ambil SEMUA kue dengan SEMUA kolomnya
async function ambilSemuaKue() {
const { data: semuaKue, error } = await supabase
.from('kue')
.select('*');
if (error) console.error("Error ambil semua kue:", error);
else console.log("Semua Kue:", semuaKue); // semuaKue akan jadi array of Kue
return semuaKue;
}
// ambilSemuaKue();
// Ambil SEMUA kue, tapi cuma kolom NAMA dan HARGA aja
async function ambilNamaDanHargaKue() {
const { data, error } = await supabase
.from('kue')
.select('nama, harga'); // Pisah koma buat milih kolom
if (error) console.error("Error:", error);
else console.log("Nama & Harga Kue:", data);
}
// ambilNamaDanHargaKue();
// Ambil SATU kue berdasarkan ID (misalnya, ID uuid string)
async function ambilKueById(idKue: string) {
const { data: kueDetail, error } = await supabase
.from('kue')
.select('*')
.eq('id', idKue) // .eq() adalah filter "equals" (sama dengan)
.single(); // .single() ngarepin cuma satu hasil, error kalau banyak atau gak ada (kecuali dihandle)
if (error && error.code !== 'PGRST116') {
console.error("Error ambil detail kue:", error);
} else if (kueDetail) {
console.log(`Detail Kue ID ${idKue}:`, kueDetail);
} else {
console.log(`Kue dengan ID ${idKue} tidak ditemukan.`);
}
return kueDetail;
}
// ambilKueById("uuid-kue-yang-valid"); // Ganti dengan ID uuid beneran
// Ambil kue yang harganya di bawah 50000 dan urutkan dari harga termurah
async function ambilKueMurah() {
const { data, error } = await supabase
.from('kue')
.select('nama, harga, kategori')
.lt('harga', 50000) // .lt() adalah filter "less than" (kurang dari)
.order('harga', { ascending: true }); // Urutkan berdasarkan harga, ascending
if (error) console.error("Error:", error);
else console.log("Kue Murah:", data);
}
// ambilKueMurah();
Beberapa Modifier Query Umum sebelum .select()
:
- Filter:
.eq('kolom', nilai)
,.neq()
,.gt()
,.gte()
,.lt()
,.lte()
,.like('kolom', '%pola%')
,.ilike()
(case-insensitive like),.in('kolom', [nilai1, nilai2])
,.is('kolom', null)
. - Pengurutan:
.order('kolom', { ascending: true/false, nullsFirst: true/false })
. - Limit & Offset (Pagination):
.limit(jumlah)
,.range(dariIndeks, sampaiIndeks)
.
3. Update (Mengubah Data yang Udah Ada / update
)
Buat ngubah data yang udah ada di tabel, kita pake metode .update()
.
- Sintaks Dasar:
await supabase.from('nama_tabel').update({ ...objekPerubahan }).eq('kolom_kondisi', nilai_kondisi);
update({ ...objekPerubahan })
: Objek yang isinya field dan nilai baru yang mau di-set.- Query filter (kayak
.eq()
) WAJIB ada setelahupdate()
buat nentuin record mana aja yang mau diubah. - Biasanya diakhiri
.select()
kalau mau dapet data yang udah diupdate.
Contoh Mengupdate Kue:
async function updatenama_kue(idKue: string, namaBaru: string) {
const { data, error } = await supabase
.from('kue')
.update({ nama: namaBaru, deskripsi_singkat: "Nama baru, deskripsi baru!" })
.eq('id', idKue)
.select()
.single();
if (error) console.error("Error update kue:", error);
else console.log("Kue berhasil diupdate:", data);
}
// updatenama_kue("uuid-kue-yang-valid", "Kue Coklat Super Enak V2");
4. Delete (Menghapus Data / delete
)
Buat ngapus record dari tabel, pake metode .delete()
. HATI-HATI YA! Ini permanen!
- Sintaks Dasar:
await supabase.from('nama_tabel').delete().eq('kolom_kondisi', nilai_kondisi);
- Query filter (kayak
.eq()
) WAJIB ada setelahdelete()
buat nentuin record mana yang mau dihapus. - Biasanya diakhiri
.select()
kalau mau dapet data yang baru aja dihapus (meskipun kadang gak perlu).
- Query filter (kayak
Contoh Menghapus Kue:
async function hapusKue(idKue: string) {
// Sebaiknya tambahkan konfirmasi atau logika bisnis sebelum benar-benar menghapus
const { data, error } = await supabase
.from('kue')
.delete()
.eq('id', idKue)
.select(); // (Opsional)
if (error) console.error("Error hapus kue:", error);
else console.log("Kue berhasil dihapus (data yang dihapus):", data);
}
// HATI-HATI SEBELUM MENJALANKAN INI!
// hapusKue("uuid-kue-yang-mau-dihapus");
Penting: Row Level Security (RLS) dan Akses Data
Ingat, Supabase sangat menekankan keamanan dengan Row Level Security (RLS).
- Kalau RLS di tabelmu aktif (dan seharusnya aktif untuk produksi!),
anon key
yang kita pake di Supabase Client ini cuma bisa ngelakuin operasi (SELECT, INSERT, UPDATE, DELETE) kalau ada Policy RLS yang ngebolehin. - Misalnya, buat
insert
data pesanan, kamu mungkin perlu bikin policy yang bilang "Pengguna yang sudah login (yangauth.uid()
-nya sama denganuser_id
di data pesanan) bolehINSERT
." - Kalau query-mu gak jalan atau ngembaliin data kosong padahal harusnya ada, selalu cek RLS Policies-mu dulu di dashboard Supabase > Authentication > Policies (atau di Table Editor > Policies).
Dengan metode-metode .insert()
, .select()
, .update()
, dan .delete()
dari Supabase JS Client, kamu udah punya "senjata" lengkap buat ngelakuin semua operasi CRUD dasar ke database Supabase-mu. Yang keren, sintaksnya lumayan mirip SQL tapi ditulis dengan gaya JavaScript yang chaining.
Jangan lupa selalu cek error
yang dikembaliin dan perhatiin RLS ya! Di bagian selanjutnya, kita bakal mulai nambahin fitur "pintu masuk" ke Toko Kue kita: Autentikasi Pengguna pake Supabase Auth!
Uji Pemahamanmu!
Memeriksa status login...