Kalau routing adalah tulang punggung Express, maka middleware adalah jantungnya. Hampir semua hal menarik di Express — parsing request body, autentikasi, logging, penanganan error — semuanya diimplementasikan sebagai middleware.
Memahami middleware adalah kunci untuk benar-benar menguasai Express.js.
Apa itu Middleware?
Middleware adalah fungsi yang memiliki akses ke objek req (request), res (response), dan fungsi next dalam siklus request-response aplikasi.
Secara sederhana, middleware adalah “kode yang berjalan di antara masuknya request dan keluarnya response”.
Request → [Middleware 1] → [Middleware 2] → [Route Handler] → Response
Setiap middleware bisa:
- Menjalankan kode apapun
- Memodifikasi objek
reqdanres - Mengakhiri siklus request-response (dengan
res.send(),res.json(), dll.) - Memanggil middleware berikutnya dengan
next()
Anatomi Fungsi Middleware
function namaMiddleware(req, res, next) {
// Lakukan sesuatu...
console.log('Middleware berjalan!');
// Teruskan ke middleware/handler berikutnya
next();
}
Perbedaan kritis: jika next() tidak dipanggil dan response tidak dikirim, request akan menggantung (hang) — browser tidak mendapat respons apapun.
Jenis-Jenis Middleware di Express
1. Application-Level Middleware
Digunakan dengan app.use() atau app.METHOD(). Berjalan untuk semua request atau path tertentu.
const express = require('express');
const app = express();
// Middleware untuk SEMUA request (tanpa path)
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// Middleware hanya untuk path /api/*
app.use('/api', (req, res, next) => {
console.log('Request ke API');
next();
});
2. Router-Level Middleware
Bekerja persis seperti application-level, tapi terikat ke instance express.Router():
const router = express.Router();
router.use((req, res, next) => {
console.log('Middleware di Router');
next();
});
router.get('/profil', (req, res) => {
res.json({ nama: 'Budi' });
});
3. Error-Handling Middleware
Middleware khusus untuk menangani error. Ditandai dengan 4 parameter: (err, req, res, next).
// Error handler harus didefinisikan TERAKHIR
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Terjadi kesalahan di server',
message: err.message
});
});
4. Built-in Middleware
Express menyertakan beberapa middleware bawaan:
// Parse JSON request body
app.use(express.json());
// Parse URL-encoded form data
app.use(express.urlencoded({ extended: true }));
// Serve static files dari folder 'public'
app.use(express.static('public'));
5. Third-Party Middleware
Middleware dari npm yang menambahkan fungsionalitas tambahan:
// Morgan - HTTP request logger
const morgan = require('morgan');
app.use(morgan('dev'));
// Helmet - mengamankan HTTP headers
const helmet = require('helmet');
app.use(helmet());
// CORS - mengizinkan cross-origin requests
const cors = require('cors');
app.use(cors());
Contoh Praktis: Membuat Middleware Kustom
Logger Sederhana
function logger(req, res, next) {
const waktu = new Date().toLocaleString('id-ID');
const method = req.method.padEnd(6);
const url = req.originalUrl;
console.log(`[${waktu}] ${method} ${url}`);
next();
}
app.use(logger);
Middleware Autentikasi Token
function autentikasi(req, res, next) {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).json({ error: 'Token tidak ditemukan' });
}
// Verifikasi token (contoh sederhana)
if (token !== 'Bearer rahasia-token-123') {
return res.status(403).json({ error: 'Token tidak valid' });
}
// Token valid — teruskan request
req.penggunaId = 1; // tambahkan data ke req
next();
}
// Terapkan hanya ke route yang butuh autentikasi
app.get('/profil', autentikasi, (req, res) => {
res.json({ id: req.penggunaId, nama: 'Budi' });
});
Middleware Validasi Request Body
function validasiPengguna(req, res, next) {
const { nama, email } = req.body;
const errors = [];
if (!nama || nama.trim().length < 2) {
errors.push('Nama minimal 2 karakter');
}
if (!email || !email.includes('@')) {
errors.push('Format email tidak valid');
}
if (errors.length > 0) {
return res.status(422).json({ errors });
}
next();
}
app.post('/pengguna', validasiPengguna, (req, res) => {
// Kode ini hanya jalan jika validasi lolos
res.status(201).json({ pesan: 'Pengguna berhasil dibuat', data: req.body });
});
Middleware Rate Limiter Sederhana
const requestCount = {};
function rateLimiter(req, res, next) {
const ip = req.ip;
const now = Date.now();
const windowMs = 60 * 1000; // 1 menit
const maxRequests = 100;
if (!requestCount[ip]) {
requestCount[ip] = { count: 1, resetTime: now + windowMs };
} else if (now > requestCount[ip].resetTime) {
requestCount[ip] = { count: 1, resetTime: now + windowMs };
} else {
requestCount[ip].count++;
}
if (requestCount[ip].count > maxRequests) {
return res.status(429).json({ error: 'Terlalu banyak request' });
}
next();
}
app.use(rateLimiter);
Di production, gunakan library
express-rate-limityang lebih robust.
Urutan Middleware Itu Penting
Express menjalankan middleware secara berurutan sesuai dengan urutan app.use():
app.use(express.json()); // 1. Parse body dulu
app.use(logger); // 2. Log request
app.use(autentikasi); // 3. Cek autentikasi
// Route handler berjalan setelah semua middleware di atas
app.get('/data', (req, res) => {
res.json({ data: 'rahasia' });
});
// Error handler SELALU terakhir
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
Melempar Error ke Error Handler
Gunakan next(err) untuk meneruskan error ke error-handling middleware:
app.get('/pengguna/:id', async (req, res, next) => {
try {
const pengguna = await Database.findById(req.params.id);
if (!pengguna) {
const err = new Error('Pengguna tidak ditemukan');
err.status = 404;
return next(err);
}
res.json(pengguna);
} catch (error) {
next(error); // teruskan ke error handler
}
});
// Error handler akan menangkapnya
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({ error: err.message });
});
Kesimpulan
Middleware adalah blok bangunan utama aplikasi Express. Dengan memahami jenis-jenisnya dan cara kerjanya, kamu bisa membangun pipeline request yang bersih — mulai dari logging, autentikasi, validasi, hingga penanganan error — semuanya terorganisir dan mudah dipelihara.
Di artikel berikutnya, kita akan bahas cara membaca data dari request: request body, query string, headers, dan file upload di Express.js.
Kiki/🎮🍉⌨️🍩💻