tutorial

114 Membuat Middleware Validasi Global untuk Input

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:

  1. Menerima request body/query/params.
  2. Memvalidasi data sesuai aturan schema.
  3. Jika valid, lanjutkan ke handler berikutnya.
  4. 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

EndpointSchema ValidasiProperti DicekOutput Ketika Invalid
POST /usersuserSchemabody400 & detail field
PUT /users/:iduserUpdateSchemabody + params.id400 & detail field
POST /articlesarticleSchemabody400 & 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 profesionalcentralize 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!

comments powered by Disqus