Persisting User Data
Membaca dan menyimpan data user-owned melalui flow async yang mengambil identity server-side, membatasi payload, serta memberi state sukses atau error.
Tujuan belajar
- Membaca urutan identity check, payload validation, query, dan UI state
- Menentukan user_id dari auth claims, bukan form payload
- Memahami kapan upsert dapat dipakai untuk satu data per user
- Menampilkan success/error state yang tidak menyembunyikan kegagalan operasi
Isi lesson
5 blok- 1.Persistensi adalah flow, bukan satu tombol SaveBelum selesaiWajib
- 2.Server Action menyimpan catatan user saat iniBelum selesaiWajib
- 3.Coding practiceBelum selesaiWajib
- 4.Checklist Local Next.js AppBelum selesaiWajib
- 5.RingkasanBelum selesaiWajib
Persistensi adalah flow, bukan satu tombol Save
WajibUntuk menyimpan data milik user, urutannya penting: ambil input, validasi bentuk dan batasnya, verifikasi identity di server, susun payload yang menyertakan user_id dari claims, jalankan query, lalu kembalikan state sukses atau error yang dapat dipahami UI. Jangan menerima user_id tersembunyi dari form sebagai sumber kebenaran.
Contoh sederhana adalah satu learning note per user. Jika tabel memiliki unique constraint untuk user_id, upsert dapat membuat row bila belum ada atau memperbaruinya bila sudah ada. Ini bukan alasan untuk mengirim seluruh object form ke database. Pilih kolom yang memang boleh diubah dan biarkan RLS menegakkan ownership sebagai lapisan tambahan.
Bagian ini memengaruhi progres lesson.
Server Action menyimpan catatan user saat ini
Wajib"use server";
import { createClient } from "@/lib/supabase/server";
export async function saveLearningNote(formData: FormData) {
const content = String(formData.get("content") ?? "").trim();
if (content.length < 1 || content.length > 500) {
return { status: "error", message: "Catatan harus berisi 1-500 karakter." };
}
const supabase = await createClient();
const { data: claimsData, error: claimsError } =
await supabase.auth.getClaims();
const userId = claimsData?.claims.sub;
if (claimsError || !userId) {
return { status: "error", message: "Masuk kembali sebelum menyimpan catatan." };
}
const { error } = await supabase
.from("learning_notes")
.upsert({ user_id: userId, content }, { onConflict: "user_id" });
if (error) {
return { status: "error", message: "Catatan belum bisa disimpan." };
}
return { status: "success", message: "Catatan tersimpan." };
}Action memvalidasi content sebelum query, lalu menentukan user_id dari claims server-side. upsert hanya cocok bila onConflict mengarah ke unique constraint yang memang ada pada tabel. Pesan yang kembali ke UI tidak membeberkan detail database. Dalam project nyata, tampilkan pending state saat action berjalan dan simpan detail error aman untuk debugging server-side.
Bagian ini memengaruhi progres lesson.
Coding practice
WajibDibuka di workspace khususPlan user data persistence flow
Susun langkah persistence untuk learning note mulai dari validasi input hingga state sukses atau error tanpa mengambil user_id dari form browser.
Tujuan awal: Fokus di tab TSX.
Practice ini dibuka di workspace khusus. Buka practice untuk memakai editor, preview, dan cek otomatis.
Tombol selesai aktif setelah semua validasi wajib lolos.
Checklist Local Next.js App
WajibSaat kamu mulai mengintegrasikan Supabase di project lokal, cek secara manual:
- Browser tidak menerima service role key atau session secret.
- Server Action atau Route Handler mengambil identity dari auth context yang diverifikasi.
- Tabel user-owned memiliki RLS dan policy untuk operasi yang benar-benar dibutuhkan.
- Submit menampilkan pending, success, dan error state yang jelas.
- Coba akses data dengan dua akun uji bila setup sudah tersedia.
FluentStack tidak dapat mengakses project atau dashboard Supabase kamu. Jangan menandai checklist ini sebagai bukti integrasi sebelum kamu benar-benar menguji app lokal.
Bagian ini memengaruhi progres lesson.
Ringkasan
Wajib- Flow persistence mengambil input, memvalidasi, memverifikasi identity, lalu menjalankan query dengan payload terbatas.
- user_id pada row user-owned berasal dari claims server-side.
- upsert memerlukan unique constraint yang sesuai dengan onConflict.
- UI perlu state pending, success, dan error; local integration tetap perlu diuji sendiri.
- Berikutnya, kamu akan memisahkan auth user dari data profile aplikasi.
Bagian ini memengaruhi progres lesson.
Langkah berikutnya
Selesaikan bagian penting lesson ini
Lanjutkan blok wajib berikutnya: Persistensi adalah flow, bukan satu tombol Save.