Middleware / Chain of Responsibility — Design Patterns

Chain of Responsibility membuat rantai handler yang memproses request satu per satu. Setiap handler bisa memproses, memodifikasi, atau menolak request sebelum…

Chain of Responsibility membuat rantai handler yang memproses request satu per satu. Setiap handler bisa memproses, memodifikasi, atau menolak request sebelum meneruskan ke handler berikutnya. Di JavaScript, ini paling dikenal sebagai middleware pattern.

Analogi: Paket pos melewati beberapa pos pemeriksaan: bea cukai → X-ray → alamat → kurir. Setiap pos bisa meneruskan, menahan, atau menolak paket.

Masalah yang diselesaikan:

Express.js Middleware — contoh paling terkenal:

const express = require("express");
const app = express();

// Middleware 1: Logging
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Teruskan ke middleware berikutnya
});

// Middleware 2: Auth check
app.use((req, res, next) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: "Unauthorized" });
    // TIDAK panggil next() — rantai berhenti
  }
  next();
});

// Middleware 3: Parse body
app.use(express.json());

// Handler terakhir
app.get("/api/data", (req, res) => {
  res.json({ data: "secret" });
});

Implementasi middleware system sendiri:

class Pipeline {
  constructor() {
    this.middlewares = [];
  }

  use(fn) {
    this.middlewares.push(fn);
    return this; // Untuk chaining
  }

  execute(context) {
    let index = 0;

    const next = () => {
      if (index < this.middlewares.length) {
        const middleware = this.middlewares[index++];
        middleware(context, next);
      }
    };

    next();
    return context;
  }
}

// Penggunaan
const pipeline = new Pipeline();

pipeline
  .use((ctx, next) => {
    ctx.timestamp = Date.now();
    next();
  })
  .use((ctx, next) => {
    ctx.user = ctx.user.toUpperCase();
    next();
  })
  .use((ctx, next) => {
    console.log("Final:", ctx);
    next();
  });

pipeline.execute({ user: "budi" });
// Final: { user: "BUDI", timestamp: 1234567890 }

Middleware untuk validasi:

function validateRequired(fields) {
  return (data, next) => {
    for (const field of fields) {
      if (!data[field]) throw new Error(`${field} wajib diisi`);
    }
    next();
  };
}

function sanitize(fields) {
  return (data, next) => {
    for (const field of fields) {
      if (typeof data[field] === "string") {
        data[field] = data[field].trim();
      }
    }
    next();
  };
}

function normalizeEmail(data, next) {
  if (data.email) {
    data.email = data.email.toLowerCase();
  }
  next();
}

const formPipeline = new Pipeline();
formPipeline
  .use(sanitize(["name", "email"]))
  .use(validateRequired(["name", "email"]))
  .use(normalizeEmail);

Middleware di dunia nyata:

Kapan pakai Middleware/Chain of Responsibility: