114 Membuat Middleware Validasi Global untuk Input
Validasi input adalah salah satu bagian terpenting dalam pengembangan aplikasi, apalagi ketika aplikasi tersebut sudah memiliki skala besar dan menerima banyak data dari berbagai endpoint. Salah satu pendekatan clean, scalable, dan maintainable untuk menangani validasi input adalah dengan membuat middleware validasi global. Dalam artikel ini, saya akan mengupas tuntas tentang cara membangun middleware validasi global, mulai dari konsep, implementasi, hingga best practise yang saya gunakan dalam proyek nyata.
Mengapa Middleware Validasi Global?
Bayangkan kamu memiliki 50 endpoint REST API, dan semuanya membutuhkan validasi input yang berbeda-beda. Jika validasi ditulis secara manual di setiap handler, bukan hanya kode menjadi berantakan, tetapi juga sangat sulit untuk dirawat dan diubah di masa depan.
Dengan middleware validasi global, kita bisa melakukan hal-hal berikut:
- Centralized validation: Semua proses validasi terpusat di satu layer.
- Reusable: Logic validasi cukup didefinisikan satu kali, bisa dipakai di seluruh handler/route.
- Consistent error response: Format error selalu konsisten.
- Easier scalability: Menambah/memodifikasi validasi lebih mudah.
Mari kita lihat flow-nya dalam bentuk diagram alur menggunakan kode mermaid.
flowchart TD
A[Client Request] --> B(Middleware Validasi Global)
B -- Input Valid --> C{Handler}
B -- Input Tidak Valid --> D[Response: 400 Bad Request]
C --> E[Response: 200 OK]
Konsep Dasar Middleware
Middleware adalah fungsi yang dijalankan di antara request masuk dan handler utama. Middleware bisa melakukan pemeriksaan, modifikasi, atau menghentikan alur request. Di beberapa framework seperti Express.js (NodeJS), middleware sudah menjadi bagian fundamental. Di dunia backend lain seperti NestJS, Laravel, hingga Go Fiber, konsep middleware juga sama.
Middleware Validasi Input
Tujuan middleware validasi adalah:
- Menerima request body/query/params.
- Memvalidasi data sesuai aturan schema.
- Jika valid, lanjutkan ke handler berikutnya.
- Jika tidak valid, kembalikan response error dengan format seragam.
Tools: Validator & Schema
Untuk validasi input, kita bisa gunakan library seperti:
- JavaScript/Node.js: Joi, Yup, Zod
- TypeScript: Zod, Yup
- Python: Pydantic, Marshmallow
- Go: go-playground/validator
Dalam artikel ini, saya akan pakai Node.js + Express + Joi karena ini paling umum dan sederhana, namun konsepnya dapat diadaptasi di bahasa lain.
Implementasi Dasar Middleware Validasi Global
1. Instal Library Validator
Install Express dan Joi:
npm install express joi
2. Definisikan Schema Validasi
Buat file schemas/userSchema.js
const Joi = require('joi');
const userSchema = Joi.object({
name: Joi.string().min(3).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).max(100).required(),
});
module.exports = {
userSchema
};
3. Global Middleware Validasi
Buat file middlewares/validate.js
function validate(schema, property = 'body') {
return function (req, res, next) {
const { error } = schema.validate(req[property], { abortEarly: false });
if (!error) {
return next();
}
// Format error response ramah API
const details = error.details.map(err => ({
field: err.path.join('.'),
message: err.message
}));
return res.status(400).json({
code: 400,
status: "error",
message: "Input validation error",
details
});
};
}
module.exports = validate;
4. Integrasi di Route
Buat file routes/user.js
const express = require('express');
const { userSchema } = require('../schemas/userSchema');
const validate = require('../middlewares/validate');
const router = express.Router();
// Middleware validasi diterapkan secara global di route ini
router.post('/', validate(userSchema), (req, res) => {
// Jika sampai sini, input valid
const user = req.body;
// Simulasi simpan user
res.status(201).json({ status: "success", data: user });
});
module.exports = router;
5. Setup Server Express
Buat file app.js
const express = require('express');
const userRoutes = require('./routes/user');
const app = express();
app.use(express.json());
app.use('/users', userRoutes);
app.listen(3000, () => {
console.log('Server jalan di http://localhost:3000');
});
Simulasi: Uji Coba Middleware
Mari tes skenario API POST /users.
a. Input Tidak Valid
Kirim:
{
"name": "B",
"email": "salah-email",
"age": 15
}
Response:
{
"code": 400,
"status": "error",
"message": "Input validation error",
"details": [
{ "field": "name", "message": "\"name\" length must be at least 3 characters long" },
{ "field": "email", "message": "\"email\" must be a valid email" },
{ "field": "age", "message": "\"age\" must be greater than or equal to 18" }
]
}
b. Input Valid
Kirim:
{
"name": "Budi",
"email": "budi@mail.com",
"age": 28
}
Response:
{
"status": "success",
"data": {
"name": "Budi",
"email": "budi@mail.com",
"age": 28
}
}
Berikut Simulasi Table Coverage
| Endpoint | Schema Validasi | Properti Dicek | Output Ketika Invalid |
|---|---|---|---|
| POST /users | userSchema | body | 400 & detail field |
| PUT /users/:id | userUpdateSchema | body + params.id | 400 & detail field |
| POST /articles | articleSchema | body | 400 & detail field |
Centralisasi validasi memudahkan kebutuhan future scaling dan konsistensi standar validasi.
Best Practises & Tips
- Modularisasi schema: Pisahkan file schema untuk tiap resource.
- Gunakan middleware secara dinamis: Middleware bisa digunakan di level route, group, atau global saat diperlukan.
- Custom error formatter: Standarisasi format error untuk frontend developer.
- Support untuk query & params: Extend middleware untuk mendukung validasi
req.query,req.params. - Integrasi testing: Uji middleware secara unit dan integration test.
Contoh ekstensi middleware untuk support body, query, params secara sekaligus:
function validateAll(schemas) {
return (req, res, next) => {
let allErrors = [];
for (const [property, schema] of Object.entries(schemas)) {
if (!schema) continue;
const { error } = schema.validate(req[property], { abortEarly: false });
if (error) {
allErrors = allErrors.concat(
error.details.map(err => ({
field: `${property}.${err.path.join('.')}`,
message: err.message
}))
);
}
}
if (allErrors.length === 0) {
return next();
}
return res.status(400).json({
code: 400,
status: "error",
message: "Input validation error",
details: allErrors
});
};
}
// Pemakaian:
// router.post('/users/:id', validateAll({
// params: idSchema,
// body: userSchema
// }), (req, res) => { ... });
Penutup
Validasi input adalah fondasi keamanan dan robustness API. Dengan membangun middleware validasi global, kamu bisa mencapai clean code, maintainability, dan standarisasi menyeluruh pada seluruh aplikasi. Jangan sampai membiarkan setiap handler menulis ulang validasi, gunakan cara engineer profesional—centralize it, test it, and scale it.
Kalau kamu punya pendekatan lain terkait validasi, silakan diskusi di kolom komentar. Jangan lupa, middleware yang baik adalah middleware yang tidak bikin stress tim kamu 6 bulan ke depan!