Checkbox yang Diam-diam Mematikan Fitur di Backend Go
Checkbox yang nggak dicentang nggak ngirim apa-apa, dan backend Go membaca keheningan itu sebagai false. Pelajaran dari bug form admin CMS.
Ringkasan
Gue kira checkbox yang nggak dicentang bakal dibiarin aja sama backend, eh ternyata Go nganggap field bool yang absen itu false. Alhasil fitur rating sama rental langsung mati pas di-save padahal nggak diapa-apain. Akhirnya gue ubah frontend biar selalu kirim true/false eksplisit dan pakai aturan null buat hapus, absen buat biarin aja.
Checkbox yang Diam-diam Mematikan Fitur
Saya lagi nambahin fitur baru di admin CMS portal WisataKota. Ada dua field boolean yang perlu ditambahkan ke entity editor: rating_enabled dengan default true, dan rental_enabled dengan default false. Awalnya saya cuma nambahin checkbox biasa di form React, berpikir ini aman aja. Logika saya waktu itu sederhana. Kalo user nggak nyentuh checkbox itu, browser nggak bakal ngirim properti apa pun ke JSON body. Saya berasumsi backend bakal melihat ketiadaan data itu sebagai "nggak ada perubahan", jadi nilai lama di database tetap utuh.
Ternyata asumsi saya salah besar.
Pas saya coba submit form dengan checkbox yang nggak dicentang, fitur rating dan penyewaan di halaman publik tiba-tiba mati. Saya sempat bingung dan langsung ngecek payload network di browser. Ternyata bener, checkbox HTML yang nggak dicentang memang nggak ngirim data sama sekali [3]. Masalahnya justru ada di cara backend Go membaca keheningan itu.
Kenapa Go Mengubah Keheningan Jadi False
Di sinilah masalah bool form zero value go benar-benar terasa dampaknya. Saat Go melakukan json.Unmarshal, dia cuma mengalokasikan struktur data yang benar-benar ada di dalam JSON tersebut. Kalau sebuah field boolean absen dari payload, Go nggak bakal membiarkannya dalam status "kosong" atau "null". Dia langsung memberi field itu zero value. Dan untuk tipe data boolean, zero value-nya adalah false [2].
Jadi, ketika form nggak mengirim rental_enabled, backend Go nggak berpikir "oh, user nggak ngubah ini, biarkan aja". Backend Go berpikir, "oh, ini boolean, nilai defaultnya false". Fitur penyewaan langsung mati diam-diam saat proses save berjalan.
Situasi ini jadi makin rumit kalo udah terbiasa pakai tag omitempty di struct Go. Tag ini memang berguna buat ngecilin ukuran JSON dengan membuang field yang dianggap kosong. Tapi untuk boolean, nilai false dianggap sebagai nilai "kosong" oleh omitempty, sehingga field itu malah di-drop saat proses marshal [1]. Ini jebakan klasik yang bikin developer sering salah kira bahwa omit berarti aman, padahal justru menghilangkan konteks penting.
Solusi: Eksplisit Itu Lebih Aman
Setelah menyadari pola ini, saya nggak bisa lagi mengandalkan asumsi "nggak dikirim berarti nggak berubah". Saya harus mengubah cara frontend dan backend berdialog agar lebih transparan.
Pertama, untuk field boolean seperti rating_enabled dan rental_enabled, saya ubah logika di React supaya selalu mengirim nilai eksplisit, baik itu true maupun false. Nggak ada lagi opsi "absen" atau undefined. Saya juga menambahkan komentar di kode backend untuk mengingatkan tim: "Bool Go yang di-omit ter-decode sebagai false — kirim eksplisit agar rating/penyewaan nggak mati diam-diam saat save".
Kedua, untuk field yang memang boleh kosong seperti type_id (tipe number atau null), saya menerapkan semantik merge-patch RFC 7396 [4]. Aturannya sederhana dan konsisten. Nilai null berarti hapus kunci tersebut dari database, sedangkan kunci yang absen berarti biarkan apa adanya. Jadi, saat mode edit, saya pastikan nilai type_id yang udah ada di-echo kembali ke form. Saat mode create, saya biarkan field itu di-omit dari payload, sehingga server bisa menyimpannya sebagai NULL sesuai desain master data.
Pengalaman ini ngubah cara saya memandang form data. Mengirim data secara eksplisit memang terasa lebih berisik di payload network, tapi jauh lebih aman daripada membiarkan bahasa pemrograman menebak maksud kita dari ketiadaan data.