Validasi Input & Arsitektur Berlapis
Tidak percaya pada input pengguna: validasi, pesan error yang jelas, dan memisahkan route, validasi, serta logika bisnis.
Aturan nomor satu: jangan percaya input
Semua yang datang dari luar — body, query, header, parameter URL — bisa salah, kosong, bertipe aneh, atau berniat jahat. Server yang baik memvalidasi sebelum memakai data. Tanpa validasi: data sampah masuk database, aplikasi crash karena undefined.toLowerCase(), dan celah keamanan terbuka.
Apa yang divalidasi?
- Keberadaan: field wajib ada.
- Tipe: string, angka, boolean — JSON tidak menjamin tipe.
- Rentang & panjang: umur 17–100, nama 3–50 karakter.
- Format: email, nomor telepon, tanggal.
- Nilai yang diizinkan: status ∈ {draft, terbit}.
Fungsi validasi murni
Tulis validasi sebagai fungsi murni (input → daftar error, tanpa efek samping). Mudah diuji dan dipakai ulang:
function validasiPengguna(body) {
const errors = {};
if (typeof body.nama !== 'string' || body.nama.trim().length < 3) {
errors.nama = 'nama minimal 3 karakter';
}
if (typeof body.email !== 'string' || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(body.email)) {
errors.email = 'email tidak valid';
}
if (!Number.isInteger(body.umur) || body.umur < 17 || body.umur > 100) {
errors.umur = 'umur harus bilangan bulat 17–100';
}
return errors; // objek kosong = valid
}
Respons validasi yang baik
Status 400 (atau 422) dengan pesan per field, agar antarmuka bisa menandai kolom yang salah:
{ "errors": { "nama": "nama minimal 3 karakter", "email": "email tidak valid" } }
Arsitektur berlapis
Memasukkan semuanya ke handler route membuat kode tak terbaca. Pisahkan berdasarkan tanggung jawab:
| Lapisan | Tugas | Contoh |
|---|---|---|
| Route | Memetakan URL ke controller | router.post('/', buat) |
| Controller | Baca request, panggil service, kirim response (HTTP saja) | function buat(req, res) |
| Validasi | Memeriksa bentuk & nilai input | validasiPengguna(body) |
| Service | Logika bisnis (tidak tahu HTTP) | tambahPengguna(data) |
| Repository / model | Akses data (database) | simpan(pengguna) |
// service — tidak mengenal req/res
const pengguna = [];
function tambahPengguna(data) {
if (pengguna.some((p) => p.email === data.email)) {
const err = new Error('email sudah terdaftar'); err.status = 409; throw err;
}
const baru = { id: pengguna.length + 1, ...data };
pengguna.push(baru);
return baru;
}
// controller — hanya urusan HTTP
function buat(req, res) {
const errors = validasiPengguna(req.body);
if (Object.keys(errors).length) return res.status(400).json({ errors });
res.status(201).json(tambahPengguna(req.body));
}
Keuntungannya: logika bisnis bisa dites tanpa menjalankan server; mengganti Express dengan framework lain atau array dengan database tidak memaksa menulis ulang semuanya.
Pustaka validasi
Untuk skema yang kompleks, pakai pustaka: Zod, Joi, Ajv (JSON Schema), atau express-validator. Prinsipnya sama dengan fungsi buatan sendiri di atas.
Sanitasi & keamanan
- Jangan pernah menggabungkan input langsung ke query SQL (injeksi SQL) — pakai query berparameter.
- Batasi ukuran body (
express.json({ limit: '100kb' })). - Hanya ambil field yang kamu harapkan (jangan
...req.bodymentah ke database — "mass assignment").