Middleware adalah fungsi yang menerima req, res, dan next, dipanggil secara berurutan oleh Express untuk setiap permintaan yang cocok. Topik ini membahas mekanisme next() sebagai penghubung antar middleware, urutan eksekusi yang ditentukan oleh urutan pendaftaran, serta cara menulis middleware buatan sendiri yang dipasang pada level aplikasi maupun pada jalur tertentu. Setelah membaca topik ini, urutan eksekusi middleware dapat diprediksi dan middleware yang dapat dipakai ulang dapat ditulis dengan tanda tangan yang benar.
Apa itu middleware ?#
Middleware adalah fungsi JavaScript dengan tiga argumen, yaitu (req, res, next). Express memanggil fungsi tersebut untuk setiap permintaan yang cocok dengan pola pendaftarannya. Bila middleware tidak mengakhiri siklus respons, ia wajib memanggil next() agar Express beralih ke middleware atau handler berikutnya. Bila next() tidak dipanggil dan res tidak diakhiri, permintaan menggantung sampai waktu habis pada sisi klien.
Pendaftaran middleware dilakukan melalui app.use(fn) untuk seluruh permintaan, app.use(path, fn) untuk yang berawalan path, atau app.METHOD(path, fn) sebagai handler rute yang sekaligus bertindak sebagai middleware terakhir. Satu pemanggilan app.use dapat menerima beberapa fungsi sekaligus, sehingga app.use(mw1, mw2, mw3) setara dengan tiga pemanggilan terpisah.
next() dan urutan eksekusi#
Express memproses daftar middleware sesuai urutan pendaftaran. Saat sebuah middleware memanggil next(), Express beralih ke entri berikutnya dengan jalur yang cocok dan menjalankan entri tersebut memakai objek req dan res yang sama. Akibatnya, data yang ditambahkan pada req oleh middleware awal dapat dibaca oleh middleware berikutnya dan oleh handler rute. Diagram berikut menggambarkan aliran permintaan lewat dua middleware menuju handler, lalu respons mengalir kembali.
Kode berikut memperlihatkan urutan tersebut secara konkret. Dua middleware memanggil next() setelah menambahkan elemen ke req.jejak, dan handler akhir membaca req.jejak sebagai respons.
// tidak-dijalankan: butuh Express yang dipasang di /tmp/pola-middleware
const express = require('express');
const app = express();
app.use((req, res, next) => {
req.jejak = ['mw1'];
console.log('mw1: lewat');
next();
});
app.use((req, res, next) => {
req.jejak.push('mw2');
console.log('mw2: lewat');
next();
});
app.get('/', (req, res) => {
console.log('handler: kirim respons');
res.json({ jejak: req.jejak });
});
const server = app.listen(0, async () => {
const port = server.address().port;
const r = await fetch(`http://127.0.0.1:${port}/`);
console.log('status:', r.status);
console.log('body:', await r.text());
server.close();
});# jalankan dari direktori topik
NODE_PATH=/tmp/pola-middleware/node_modules node chain.jsmw1: lewat
mw2: lewat
handler: kirim respons
status: 200
body: {"jejak":["mw1","mw2"]}Keluaran memperlihatkan urutan mw1, mw2, handler, dan akhirnya status serta body yang dikirim. Larik jejak bernilai ["mw1","mw2"] karena setiap middleware menambah elemen ke objek req yang sama, lalu membaca hasilnya di handler. Hal ini mengonfirmasi bahwa next() menyerahkan kontrol tanpa membuat objek permintaan baru.
Middleware per jalur dan per method#
Pemanggilan app.use(path, fn) membatasi middleware hanya pada permintaan yang jalurnya berawalan path. Middleware semacam itu cocok untuk pemeriksaan otorisasi pada sekelompok rute, tanpa perlu mengulang pemeriksaan pada setiap handler. Bila middleware memutuskan mengakhiri siklus, misalnya mengirim status 401, ia harus mengembalikan dari fungsi agar next() yang mungkin ada di bawahnya tidak ikut terpanggil.
// tidak-dijalankan: butuh Express yang dipasang di /tmp/pola-middleware
const express = require('express');
const app = express();
// middleware pencatat: berjalan untuk semua rute
app.use((req, res, next) => {
console.log(`log: ${req.method} ${req.url}`);
next();
});
// middleware pemeriksa token: hanya berlaku pada /rahasia
app.use('/rahasia', (req, res, next) => {
const token = req.query.token;
if (token !== 'abc123') {
console.log('auth: tolak, token tidak valid');
return res.status(401).json({ error: 'token tidak valid' });
}
console.log('auth: lolos');
next();
});
app.get('/rahasia', (req, res) => {
res.json({ rahasia: 'nilai penting' });
});
app.get('/publik', (req, res) => {
res.json({ publik: true });
});
const server = app.listen(0, async () => {
const port = server.address().port;
const a = await fetch(`http://127.0.0.1:${port}/publik`);
console.log('/publik ->', a.status, await a.text());
const b = await fetch(`http://127.0.0.1:${port}/rahasia`);
console.log('/rahasia (tanpa token) ->', b.status, await b.text());
const c = await fetch(`http://127.0.0.1:${port}/rahasia?token=abc123`);
console.log('/rahasia (token benar) ->', c.status, await c.text());
server.close();
});# jalankan dari direktori topik
NODE_PATH=/tmp/pola-middleware/node_modules node auth-log.jslog: GET /publik
/publik -> 200 {"publik":true}
log: GET /rahasia
auth: tolak, token tidak valid
/rahasia (tanpa token) -> 401 {"error":"token tidak valid"}
log: GET /rahasia?token=abc123
auth: lolos
/rahasia (token benar) -> 200 {"rahasia":"nilai penting"}Keluaran memperlihatkan dua perilaku penting. Middleware pencatat berjalan untuk seluruh rute, termasuk /publik, karena dipasang tanpa argumen jalur. Middleware pemeriksa token hanya berjalan untuk /rahasia dan menghentikan siklus dengan return res.status(401)... saat token salah, sehingga handler app.get('/rahasia') tidak dipanggil. Kata kunci return pada respons tolakan menghindari pemanggilan next() tidak sengaja setelah res sudah diakhiri.
Menulis middleware sendiri#
Middleware dapat ditulis sebagai fungsi biasa dan dipasang ulang di banyak aplikasi. Pola yang lazim adalah menerima opsi konfigurasi melalui closure, sehingga perilaku middleware dapat disesuaikan tanpa mengubah fungsinya.
// tidak-dijalankan: fragmen, bukan berkas utuh
function pencatat({ label = 'app' } = {}) {
return function (req, res, next) {
console.log(`[${label}] ${req.method} ${req.url}`);
next();
};
}
// pemakaian:
// app.use(pencatat({ label: 'api' }));
Middleware yang tidak memanggil next() dan tidak mengakhiri respons membuat permintaan menggantung. Pastikan setiap cabang logika, baik sukses maupun gagal, berakhir dengan salah satu dari next(), res.send(), res.json(), atau res.end().
Kesalahan umum#
Lupa memanggil next() pada middleware yang tidak mengakhiri respons. Akibatnya, permintaan menggantung sampai klien kehabisan waktu, dan handler rute tidak pernah dijalankan. Pastikan setiap middleware yang bukan handler terakhir memanggil next() di setiap cabang yang tidak mengirim respons.
Mendaftarkan middleware setelah handler rute yang relevan. Karena Express memproses daftar secara berurutan, app.use(logger) yang didaftarkan setelah app.get('/users', ...) tidak akan berjalan untuk permintaan /users. Pasang middleware umum sebelum rute, dan pasang middleware jalur sebelum rute pada jalur tersebut.
Memanggil next() setelah mengirim respons. Setelah res.json dipanggil, header respons sudah dikirim, sehingga next() yang mengarah ke middleware lain yang juga mencoba res.send memicu error. Gunakan return sebelum res.send atau next agar cabang berhenti tepat pada satu titik.
Ringkasan#
- Middleware adalah fungsi
(req, res, next)yang dipanggil secara berurutan oleh Express, dengannext()sebagai penghubung ke entri berikutnya. - Urutan eksekusi ditentukan oleh urutan pendaftaran, sehingga middleware umum dipasang sebelum rute dan middleware jalur dipasang sebelum rute pada jalur tersebut.
app.use(path, fn)membatasi middleware pada awalan jalur tertentu, sedangkanapp.use(fn)berlaku untuk seluruh permintaan.- Middleware yang dapat dipakai ulang ditulis sebagai fungsi biasa, sering kali dengan opsi konfigurasi melalui closure, dan wajib memanggil
next()atau mengakhiri respons pada setiap cabang.
Referensi#
- Express — Using middleware — Express.js — Penjelasan resmi tentang jenis middleware dan cara memasangnya pada aplikasi maupun jalur.
- Express — Writing middleware — Express.js
— Panduan resmi menulis middleware sendiri, mencakup tanda tangan
req, res, nextdan penggunaannext(). - Express 5.x API — app.use — Express.js
— Referensi
app.use, urutan eksekusi, dan sintaks pendaftaran beberapa middleware sekaligus.