Skip to content

Tiga Bentuk Nullable di Write Path Go

Adityo Guni Waluyo

Meneruskan kolom nullable baru lewat CRUD Go: idiom PUT, bool polos, pointer ID, dan kontrak null eksplisit.

Ringkasan

KotaPortal nambah empat kolom baru lewat CRUD yaitu kontak, dua toggle rating dan rental, serta ID tipe. Kontak dikosongin pake string kosong biar jadi null, ID tipe pake pointer biar nil jadi null, toggle bool biasa dan PUT itu nulis penuh. Respons tanpa omitempty jadi null tetap muncul dan ada validasi kontak max 100 karakter serta ID minimal satu.

Saya sedang membaca diff commit di service Go untuk fitur admin KotaPortal. Ada empat kolom baru yang harus diteruskan lewat CRUD: data kontak, dua toggle (rating dan rental), plus ID tipe. Tiga bentuk data sekaligus di satu upsert.

Insting pertama saya dulu: bikin semua field jadi pointer biar bisa null, atau kasih omitempty di setiap response JSON supaya datanya terlihat rapi. Saya juga sempat berasumsi bahwa jika field tidak dikirim dalam request PUT, nilainya akan tetap dipertahankan seperti konsep PATCH.

Tebakan itu meleset di dua titik. Di repo ini, PUT berarti tulis penuh; menghapus field dari request bukan berarti mempertahankan nilai lama. Dan kalau kita pakai omitempty di response, klien yang men-generate kode dari OpenAPI bakal kehilangan key-nya sama sekali, bukan melihatnya bernilai null.

Bentuk Pertama: String yang Mengikuti Idiom Telepon

Data kontak itu string opsional yang mengikuti idiom "empty string = kosongkan". Di model, kita pakai NullString dari package database/sql, tipe yang memang dirancang untuk kolom yang boleh null lengkap dengan flag Valid sebagai penentu [7]. Di request, dia datang sebagai string biasa. Kalau kosong, flag Valid di-set false agar tersimpan sebagai NULL di database. Persis seperti perilaku kolom telepon yang sudah ada sebelumnya.

Bentuk Kedua dan Ketiga: Bool Polos dan Pointer ID

Toggle rating dan rental adalah bool polos. Kedua kolom ini didefinisikan NOT NULL dengan default value di database, artinya di request mereka selalu membawa nilai dan tidak pernah null. Perbedaan menarik di antara keduanya cuma terletak pada default-nya saja: satu true, satu false.

ID tipe beda lagi. Ini ditangani sebagai pointer int64 di request dan NullInt64 di model [7]. Kalau pointer-nya nil, sistem menerjemahkannya menjadi NULL dengan cara yang sama seperti bentuk pertama. Bedanya, tidak ada trik string kosong: nil memang satu-satunya cara menyatakan kosong.

Kontrak Response: Null yang Eksplisit

Di sisi response, kita menggunakan pointer tanpa omitempty. Alasannya sederhana. Saat kolom dikosongkan, JSON yang dihasilkan harus tetap menulis contact_person null atau type_id null, bukan menghilangkan key tersebut. Integration test round-trip mengunci ini dengan assert substring persis. Kontrak seperti ini bisa diandalkan oleh klien yang di-generate otomatis dari OpenAPI, karena key selalu ada.

Prinsipnya sejajar dengan konvensi desain API: field opsional tidak boleh diasumsikan ada oleh pembaca, dan nilai default hanya sah diterapkan pada field opsional [4]. Serialisasi nullable di Go memang membutuhkan pointer tanpa omitempty agar nilai null-nya muncul secara eksplisit [4].

// Model: tipe database yang bisa NULL
type EntityModel struct {
    ContactPerson sql.NullString
    TypeID        sql.NullInt64
    RatingEnabled bool
}

// Request: string biasa untuk idiom kosong, pointer untuk ID
type UpsertRequest struct {
    ContactPerson string
    TypeID        *int64
    RatingEnabled bool
}

// Response: pointer TANPA omitempty agar null tetap muncul
type EntityResponse struct {
    ContactPerson *string
    TypeID        *int64
    RatingEnabled bool
}

Dua validasi baru juga berdiri di pintu masuk service: data kontak dibatasi maksimal 100 karakter, dan ID tipe harus minimal satu karena nol dicadangkan, tanpa pemeriksaan referensial sampai tabel masternya mendarat di slice berikutnya.

Saya memilih pola ini karena memberikan kejelasan mutlak. Klien tidak perlu menebak apakah data hilang karena error, belum di-set, atau memang sengaja dikosongkan. Null yang eksplisit adalah jawaban yang paling jujur.

Sources

[7] Go database/sql package [4] Kubernetes API Conventions

Artikel terkait