CRUD dengan Supabase JS Client

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):
    typescripttypescript
    // 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) dan error (isinya objek error kalau ada masalah). Selalu cek error-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:

typescripttypescript
// 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() setelah insert(): Kalau kamu mau dapet balik data yang baru aja diinsert (lengkap sama id dan created_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:

typescripttypescript
// 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 setelah update() buat nentuin record mana aja yang mau diubah.
    • Biasanya diakhiri .select() kalau mau dapet data yang udah diupdate.

Contoh Mengupdate Kue:

typescripttypescript
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 setelah delete() buat nentuin record mana yang mau dihapus.
    • Biasanya diakhiri .select() kalau mau dapet data yang baru aja dihapus (meskipun kadang gak perlu).

Contoh Menghapus Kue:

typescripttypescript
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 (yang auth.uid()-nya sama dengan user_id di data pesanan) boleh INSERT."
  • 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...

Bikin, Baca, Update, Hapus Data di Supabase Pake Supabase JS Client dengan Mudah! - Dasar Supabase (Backend as a Service) - Dasar Supabase (Backend as a Service)