Database adalah Last Line of Defense
Waktu saya implement endpoint register di auth API yang saya buat, saya awalnya hanya cek email di kode:
go
err = ah.db.QueryRow("SELECT email FROM users WHERE email = $1", req.Email).Scan(&emailExists)
if err == nil {
c.JSON(http.StatusConflict, gin.H{"error": "Email already registered"})
return
}Logikanya benar — cek dulu, kalau ada tolak, kalau tidak ada baru insert. Tapi ada celah yang tidak saya pikirkan: bagaimana kalau dua request dengan email yang sama datang hampir bersamaan?
Keduanya SELECT di saat database belum punya datanya. Keduanya lolos. Keduanya INSERT. Hasilnya dua user dengan email yang sama.
Solusinya bukan hanya di kode — tapi di database:
sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
);Satu kata: UNIQUE. Database yang enforce — bukan kode.
Validasi di kode tetap ada, untuk pesan error yang jelas ke user. Tapi integritas data dijaga di database. Karena kode bisa punya celah, race condition bisa terjadi — database tidak peduli semua itu. Dia hanya tahu satu aturan: email ini harus unik.
Pelajaran yang saya ambil: atur constraint di ERD sebelum menulis satu baris kode pun. Jangan andalkan kode untuk sesuatu yang bisa dijaga di level yang lebih dalam.