Bedah form donasi dari UI sampai Zod server: amount, nama, kontak opsional, metode bayar, consent, dan bukti bahwa validasi tidak bergantung pada atribut HTML.
35m bacaanKuis WajibMicro Badge Lesson
Form Donasi + Validasi Server
Di halaman donasi, form bisa terlihat rapi: ada nominal, nama, email, WhatsApp, metode pembayaran, tombol submit, dan checkbox persetujuan.
Tapi tampilan rapi belum membuktikan data aman.
Dalam aplikasi donasi, input dari browser harus dianggap mentah. User bisa membuka DevTools, mengubah atribut HTML, mengirim request manual, atau mengosongkan field yang di UI terlihat wajib. Karena itu, pertanyaan utamanya bukan “form-nya sudah bagus belum?”, tapi:
text
Apakah server menolak data yang tidak sah sebelum menyentuh database?
min={10000} dan required membantu user di browser. Tapi itu bukan pagar keamanan. Atribut HTML bisa dihapus dari DevTools.
Karena itu, server tetap harus punya aturan sendiri.
Schema Zod sebagai kontrak input
Buka:
text
src/lib/validations/donation.ts
Untuk nominal, schema-nya:
ts
amount: z.coerce .number() .int('Nominal harus bilangan bulat') .min(10_000, 'Minimum donasi Rp10.000') .max(100_000_000, 'Maksimum donasi Rp100.000.000'),
Ada empat keputusan penting di sini.
Pertama, FormData mengirim value sebagai string. Karena itu dipakai z.coerce.number(), bukan langsung z.number().
Kedua, .int() menolak nominal desimal seperti 50000.5. Donasi Rupiah di flow ini memakai bilangan bulat.
Ketiga, .min(10_000) dan .max(100_000_000) membuat batas bisnis berada di server.
Keempat, pesan error ditulis untuk manusia, bukan hanya “invalid input”.
Kontak opsional tidak berarti bebas format
Schema email:
ts
donorEmail: z .union([z.string().email('Email tidak valid'), z.literal('')]) .transform((v) => (v === '' ? null : v)),
Ini pola yang sering bikin bingung.
Kenapa tidak cukup .optional()? Karena field kosong dari form biasanya datang sebagai string kosong '', bukan undefined. Kalau kita memakai z.string().email().optional(), string kosong tetap dianggap string dan gagal email. Dengan union(..., z.literal('')), field boleh dikosongkan. Lalu transform mengubahnya menjadi null, supaya database tidak diisi string kosong.
Untuk WhatsApp juga sama: kosong boleh, tapi kalau diisi harus sesuai format nomor Indonesia.
Perhatikan posisinya: validasi terjadi sebelum createClient(), sebelum query campaign, dan sebelum insert donasi. Kalau input invalid, database belum disentuh.
Ini yang ingin kamu buktikan.
Prompt untuk Antigravity
text
Mulai dari AGENTS.md, DESIGN.md, dan docs/README.md.Tugas analisis, jangan mengubah file.Baca:- src/components/donation/donation-form.tsx- src/lib/validations/donation.ts- src/app/campaigns/[slug]/donate/actions.tsJelaskan kontrak validasi form donasi:1. field apa saja yang dikirim form,2. aturan Zod untuk amount, donorName, donorEmail, donorPhone, paymentMethod, isAnonymous, dan consent,3. kenapa validasi HTML tidak cukup,4. di mana safeParse dijalankan sebelum database disentuh,5. tiga kasus input yang harus ditolak server.Untuk setiap klaim penting, kutip potongan kode sumbernya.Jangan menambah field, jangan mengubah schema, jangan mengubah UI.
Prompt ini sengaja analitis. Kalau fitur sudah ada, kamu tidak perlu meminta AI membuat ulang. Kamu perlu meminta AI membaca bukti.
Kalau ingin membuktikan server yang menolak, hapus atribut min atau required dari DevTools, lalu submit lagi. Error tetap harus muncul dari state server action.
Prinsip yang perlu dibawa
Form bukan sumber kebenaran. Form adalah antarmuka.
Sumber kebenaran input ada di server:
text
FormData mentah → schema Zod → fieldErrors atau data sah → lanjut database
Kalau agent hanya memperbaiki tampilan tanpa memeriksa safeParse, ia belum menyelesaikan masalah validasi. Di aplikasi donasi, error validasi harus terjadi sebelum uang, status, atau nomor donasi diproses.
Checkpoint
Kamu bisa menyebut aturan amount dari schema Zod.
Kamu paham kenapa email/WhatsApp kosong diubah menjadi null.
Kamu tahu posisi safeParse sebelum database.
Nominal di bawah Rp10.000 ditolak server.
Nominal desimal ditolak server.
Consent kosong ditolak server.
Kamu tidak mengandalkan atribut HTML sebagai keamanan.
Kalau tadi kamu merasa “lah, form kan sudah ada”, justru di situlah pelajarannya. Dalam AI coding, fitur yang sudah terlihat selesai tetap harus dibaca kontraknya. Form donasi baru layak dipercaya setelah kamu tahu server benar-benar menolak input yang tidak sah.
Checkpoint KuisTarget: 70%
Kuis 04.1 — Form Donasi + Validasi Server
Kuis ini wajib untuk sertifikat. Kamu tetap bisa lanjut ke pelajaran berikutnya dan mengerjakannya nanti.