Tutorial Web Development · ±14 menit baca 10 Menit Baca 11 Agustus 2026 8 Dilihat

Supabase Auth & Database untuk Aplikasi Nuxt: Panduan Praktis

Beranda › Tutorial › Nuxt › Supabase

Supabase Auth & Database untuk Aplikasi Nuxt: Panduan Praktis#

Diterbitkan 9 Oktober 2026 · Kategori: Tutorial Web Development · ±14 menit baca

Bayangkan Anda sedang membangun aplikasi baru. Anda butuh database, sistem login, penyimpanan file, dan pembaruan data langsung. Biasanya itu berarti empat layanan, empat dokumentasi, dan berminggu-minggu menyambungkannya. Dengan Nuxt dan Supabase, semuanya bisa berjalan dalam satu sore.

Prolog

Kenapa Nuxt dan Supabase Cocok Dipasangkan?#

Supabase menjadi favorit saya untuk urusan backend karena menawarkan database PostgreSQL, autentikasi, dan storage dalam satu platform. Menggabungkannya dengan Nuxt terasa natural: Nuxt memberi Anda rendering sisi server, routing berbasis file, dan server routes, sedangkan Supabase menyediakan fondasi data yang kuat di belakangnya.

Alasan praktisnya sederhana. Pertama, Supabase memakai PostgreSQL asli, jadi Anda tidak terkunci pada format data yang aneh. Kedua, autentikasi sudah tersedia tanpa Anda menulis sistem hash password sendiri. Ketiga, ada modul resmi komunitas, @nuxtjs/supabase, yang membungkus klien Supabase menjadi composable dan helper server khas Nuxt.

Panduan ini cocok untuk Anda yang sudah mengenal dasar Nuxt 3 dan ingin tahu cara memakai Supabase dengan benar, terutama dari sisi keamanan. Kita akan membahas empat hal: setup dan kunci API, autentikasi dan session, Row Level Security, lalu realtime. Di akhir ada langkah mulai cepat dan daftar kesalahan umum.

Inti

1. Menyiapkan Proyek Nuxt dan Supabase#

Buat proyek di dashboard Supabase, lalu salin URL proyek dan kunci API dari pengaturan API. Di sisi Nuxt, pasang modulnya:

CODE
npx nuxi module add supabase

Lalu isi file .env:

CODE
SUPABASE_URL=https://xxxx.supabase.co
SUPABASE_KEY=sb_publishable_xxxxxxxx
SUPABASE_SERVICE_KEY=kunci-rahasia-server

Catatan: Supabase kini memperkenalkan format kunci baru (publishable key dan secret key) di samping kunci lama anon dan service_role. Nama variabel lingkungan yang dipakai modul bisa berubah antarversi, jadi cocokkan dengan dokumentasi modul sebelum menyalin contoh di atas.

Memahami dua jenis kunci#

Ini bagian yang paling sering disalahpahami pemula, jadi kita bahas pelan-pelan.

KunciDipakai diPerilaku terhadap RLS
Publishable (atau anon)Klien/browser dan kebutuhan publikTunduk pada RLS
Secret (atau service_role)Hanya serverMelewati RLS sepenuhnya

Di Nuxt, saya biasanya memakai service role key di sisi server untuk akses aman, sementara publishable key dipakai untuk kebutuhan publik. Kuncinya ada pada kata "server": kunci rahasia tidak boleh pernah masuk ke bundle klien. Jangan menaruhnya di runtimeConfig.public dan jangan memberi awalan NUXT_PUBLIC_. Karena kunci ini melewati semua policy, kebocorannya sama dengan memberi akses penuh ke database Anda.

2. Autentikasi dan Session di Nuxt#

Konsep kunci autentikasi adalah session. Setelah pengguna berhasil login, Supabase menerbitkan access token (JWT berumur pendek) dan refresh token. SDK JavaScript menyimpan dan memperbarui keduanya otomatis, dan SDK ini bisa dipakai di sisi klien maupun server. Modul Nuxt menyinkronkan session lewat cookie, sehingga halaman yang dirender di server pun tahu siapa penggunanya.

Form login di sisi klien#

CODE
<script setup lang="ts">
const client = useSupabaseClient()
const email = ref('')
const password = ref('')
const errorMsg = ref('')

async function login() {
  const { error } = await client.auth.signInWithPassword({
    email: email.value,
    password: password.value,
  })
  if (error) errorMsg.value = error.message
  else await navigateTo('/dashboard')
}
</script>

Pilihan metode login tidak hanya email dan password. Supabase juga mendukung magic link lewat signInWithOtp, login sosial seperti Google dan GitHub lewat signInWithOAuth, serta login telepon. Untuk produk yang menyasar pengguna awam, magic link sering menurunkan hambatan karena tidak ada password yang harus diingat.

Membaca pengguna yang sedang login#

CODE
<script setup lang="ts">
const user = useSupabaseUser()
</script>

<template>
  <p v-if="user">Halo, {{ user.email }}</p>
  <NuxtLink v-else to="/login">Masuk</NuxtLink>
</template>

Melindungi halaman#

Modul menyediakan opsi redirect di konfigurasi, tetapi Anda juga bisa membuat route middleware sendiri:

CODE
// middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const user = useSupabaseUser()
  if (!user.value) return navigateTo('/login')
})

Penting: middleware hanya melindungi tampilan, bukan data. Orang yang mahir tetap bisa memanggil API Supabase langsung dari konsol browser. Perlindungan data yang sebenarnya ada di Row Level Security, topik berikutnya.

Auth di server route#

Untuk endpoint di folder server/api, gunakan helper server dari modul. Dengan begitu pengecekan identitas terjadi sebelum data disentuh:

CODE
// server/api/profile.get.ts
import { serverSupabaseUser, serverSupabaseClient } from '#supabase/server'

export default defineEventHandler(async (event) => {
  const user = await serverSupabaseUser(event)
  if (!user) throw createError({ statusCode: 401, statusMessage: 'Belum login' })

  const client = await serverSupabaseClient(event)
  const { data, error } = await client.from('profiles').select('*').single()
  if (error) throw createError({ statusCode: 500, statusMessage: error.message })
  return data
})

serverSupabaseClient bekerja atas nama pengguna yang login, jadi RLS tetap berlaku. Sebaliknya, serverSupabaseServiceRole melewati RLS. Pakai yang kedua hanya untuk tugas administratif yang memang butuh akses penuh, misalnya cron job, webhook pembayaran, atau migrasi data, dan selalu validasi masukan sebelum menyentuh database.

3. Row Level Security: Pintu Keamanan per Baris Data#

Database Supabase datang dengan Row Level Security (RLS), yang bisa diibaratkan sebagai pintu keamanan per baris data. Tanpa RLS, siapa pun yang memegang publishable key bisa membaca seluruh isi tabel lewat API otomatis Supabase. Dengan RLS aktif dan tanpa policy, tidak ada baris yang bisa diakses oleh klien, jadi Anda memulai dari posisi paling aman lalu membuka akses seperlunya.

Setiap kali membuat tabel baru, saya selalu mengaktifkan RLS dan membuat policy yang jelas: siapa yang boleh membaca, menulis, dan menghapus. Berikut contoh tabel profiles:

CODE
create table public.profiles (
  id uuid primary key references auth.users (id) on delete cascade,
  username text unique,
  avatar_url text,
  created_at timestamptz default now()
);

alter table public.profiles enable row level security;

create policy "Pengguna membaca profilnya sendiri"
on public.profiles for select
to authenticated
using ( (select auth.uid()) = id );

create policy "Pengguna memperbarui profilnya sendiri"
on public.profiles for update
to authenticated
using ( (select auth.uid()) = id )
with check ( (select auth.uid()) = id );

Beberapa hal layak dicatat. auth.uid() mengembalikan ID pengguna dari JWT yang sedang aktif. Membungkusnya dengan (select ...) adalah praktik yang dianjurkan dokumentasi Supabase agar hasilnya di-cache per kueri dan performa tidak turun di tabel besar. Klausa using menentukan baris mana yang terlihat atau bisa dimodifikasi, sedangkan with check memvalidasi data baru yang ditulis.

Pola policy yang sering dipakai#

  • Data pribadi: baris hanya milik pemiliknya (user_id = auth.uid()), seperti catatan, pengaturan, dan keranjang.
  • Data publik, tulis terbatas: semua orang boleh membaca, hanya pemilik yang boleh mengubah, seperti artikel dan profil publik.
  • Berbasis peran: simpan peran di tabel terpisah dan periksa lewat fungsi, bukan lewat data yang bisa diubah pengguna sendiri.
  • Multi-tenant: baris dibatasi berdasarkan keanggotaan organisasi atau tim.

Membuat profil otomatis saat pendaftaran#

Agar tabel profiles terisi tanpa kode tambahan di aplikasi, pakai trigger pada auth.users:

CODE
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = ''
as $$
begin
  insert into public.profiles (id) values (new.id);
  return new;
end;
$$;

create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();

Tips pengujian: jangan menguji RLS hanya dari SQL Editor dashboard, karena editor itu berjalan dengan hak tinggi. Uji dari aplikasi Anda sendiri dengan dua akun berbeda. Coba baca dan ubah data milik akun lain. Jika berhasil, policy Anda bocor.

4. Realtime: Data yang Selalu Segar#

Yang paling saya suka adalah kemampuan realtime. Dengan sedikit konfigurasi, perubahan data bisa langsung tersinkron ke antarmuka pengguna. Fitur ini cocok untuk notifikasi, dashboard yang selalu ter-update, obrolan, atau papan kolaborasi.

Langkah pertama, aktifkan realtime untuk tabel yang dibutuhkan:

CODE
alter publication supabase_realtime add table public.notifications;

Langkah kedua, berlangganan perubahan dari komponen Nuxt:

CODE
<script setup lang="ts">
const client = useSupabaseClient()
const items = ref<any[]>([])
let channel: ReturnType<typeof client.channel>

onMounted(() => {
  channel = client
    .channel('notifikasi')
    .on(
      'postgres_changes',
      { event: 'INSERT', schema: 'public', table: 'notifications' },
      (payload) => items.value.unshift(payload.new)
    )
    .subscribe()
})

onBeforeUnmount(() => {
  if (channel) client.removeChannel(channel)
})
</script>

Dua kebiasaan baik di sini: selalu lepas langganan saat komponen dilepas agar tidak terjadi kebocoran koneksi, dan ingat bahwa RLS juga berlaku pada realtime. Pengguna hanya menerima perubahan pada baris yang boleh mereka baca. Perlakukan realtime sebagai pelengkap: ambil data awal lewat kueri biasa, lalu biarkan langganan menjaga daftar tetap mutakhir.

5. Praktik Terbaik untuk Produksi#

  • Aktifkan RLS di semua tabel pada skema public, tanpa pengecualian.
  • Simpan kunci rahasia hanya di server dan putar (rotate) bila dicurigai bocor.
  • Tambahkan indeks pada kolom yang dipakai di policy, seperti user_id, agar kueri tetap cepat.
  • Gunakan migrasi lewat Supabase CLI agar skema dan policy tercatat di Git.
  • Buat tipe TypeScript dari skema dengan supabase gen types supaya kueri Anda aman tipe.
  • Pisahkan lingkungan pengembangan dan produksi dengan proyek Supabase yang berbeda.

6. Kesalahan Umum yang Perlu Dihindari#

Melupakan RLS pada tabel baru#

Ini penyebab paling sering kebocoran data. Biasakan membuat tabel dan mengaktifkan RLS dalam satu migrasi yang sama.

Memakai service role di klien#

Kadang terjadi karena ingin "cepat jalan". Hasilnya, semua policy tidak berarti. Jika sebuah operasi butuh hak tinggi, pindahkan ke server route.

Mengandalkan middleware sebagai satu-satunya pertahanan#

Middleware menyembunyikan halaman, sedangkan RLS menjaga data. Anda butuh keduanya.

Menaruh peran pengguna di metadata yang bisa diubah sendiri#

Bila pengguna dapat mengubah user_metadata mereka, peran yang disimpan di sana bisa dipalsukan. Simpan otorisasi di tempat yang hanya bisa diubah server.

Epilog

Mulai dari Hal Kecil, Selesaikan End-to-End#

Jika Anda baru mencoba kombinasi Nuxt dan Supabase, mulailah dari hal kecil. Buat tabel profiles, atur RLS, lalu tambahkan form login. Dengan tiga langkah itu, Anda sudah menyentuh database, keamanan, dan autentikasi sekaligus.

  1. Buat proyek Supabase dan pasang modul @nuxtjs/supabase.
  2. Buat tabel profiles, aktifkan RLS, dan tulis dua policy di atas.
  3. Bangun halaman login dan satu halaman yang dilindungi.
  4. Uji dengan dua akun untuk memastikan data tidak bocor.
  5. Tambahkan satu fitur realtime, misalnya notifikasi.

Dari situ Anda akan merasakan betapa cepatnya membangun produk digital end-to-end. Tidak perlu menguasai semuanya hari ini. Satu tabel, satu policy, dan satu form login sudah cukup untuk mengubah rasa penasaran menjadi prototipe yang berjalan.

Langkah berikutnya: buka dashboard Supabase Anda, buat tabel pertama, dan jalankan kode di artikel ini. Jika Anda menemukan kendala, tuliskan di kolom komentar.

Pertanyaan yang Sering Diajukan (FAQ)#

Apakah Supabase gratis untuk Nuxt?#

Supabase menyediakan paket gratis dengan batas tertentu. Karena ketentuan dan batasnya bisa berubah, periksa halaman harga resmi sebelum merencanakan produksi.

Apakah publishable key aman dibagikan di klien?#

Ya, kunci itu memang dirancang untuk publik, asalkan RLS aktif dan policy Anda benar. Kunci rahasia tidak boleh dibagikan.

Perlukah memakai modul @nuxtjs/supabase?#

Tidak wajib. Anda bisa memakai @supabase/supabase-js langsung. Modul hanya menghemat konfigurasi dan menyediakan composable serta helper server.

Sumber dan Bacaan Lanjutan#

  1. Dokumentasi Supabase: supabase.com/docs
  2. Supabase Auth: supabase.com/docs/guides/auth
  3. Row Level Security di Supabase: supabase.com/docs/guides/database/postgres/row-level-security
  4. Supabase Realtime: supabase.com/docs/guides/realtime
  5. Modul Nuxt Supabase: supabase.nuxtjs.org dan github.com/nuxt-modules/supabase
  6. Dokumentasi Nuxt: nuxt.com/docs
  7. Row Security Policies di PostgreSQL: postgresql.org/docs/current/ddl-rowsecurity.html

Nama fitur, variabel lingkungan, dan antarmuka dashboard dapat berubah. Verifikasi detail terbaru di dokumentasi resmi sebelum dipakai di produksi.

SupabaseNuxt 3AutentikasiRow Level SecurityRealtimePostgreSQL

Alfin Almustajab
Alfin AlmustajabFull-Stack Developer, UI/UX Designer & AI Specialist

Membangun aplikasi web berperforma tinggi, sistem cerdas terintegrasi AI, dan antarmuka interaktif modern.

Artikel Monolog Lainnya

Lihat Semua
Terbuka untuk kolaborasi
LinkedInGitHubEmailWhatsApp

© 2026 ALFIN ALMUSTAJAB © . All rights reserved.