Middleware adalah alasan utama framework terasa lebih rapi dibanding server polos: satu potong kode bisa berjalan untuk banyak rute sekaligus tanpa disalin-salin. Logging, pemeriksaan token, parsing body, dan penyajian berkas statis semuanya dikerjakan middleware. Pada Chapter ini kita akan memahami cara kerja pipeline middleware di Express, menulis middleware buatan sendiri, lalu memakai middleware bawaan express.static untuk menyajikan berkas dari folder proyek.

Konsep Pipeline #

Setiap permintaan yang masuk ke aplikasi Express berjalan menyusuri deretan fungsi middleware, dari yang didaftarkan paling awal hingga menemukan rute yang cocok. Setiap middleware menerima tiga argumen: req, res, dan next. Dengan memanggil next(), middleware menyerahkan kendali ke middleware berikutnya; dengan mengirim respons lewat res, middleware memotong perjalanan permintaan di titik tersebut.

Diagram di atas menggambarkan pipeline yang akan kita bangun: logger mencatat, body parser mengurai, pemeriksa token memutuskan, dan handler rute menyelesaikan. Penolakan di titik mana pun menghentikan tahap setelahnya.

Middleware Buatan Sendiri #

Berkas berikut menyusun pipeline dari tiga middleware — pencatat log, pemeriksa token untuk path tertentu, dan body parser — lalu mendemonstrasikan next('route') untuk melompat antar handler dengan path sama.

js
import express from 'express';

const host = '127.0.0.1';
const app = express();

// Middleware 1: pencatat log sederhana.
const log = [];
app.use((req, res, next) => {
  log.push(`${req.method} ${req.path}`);
  next();
});

// Middleware 2: pemeriksa token, hanya untuk path berawalan /rahasia.
app.use('/rahasia', (req, res, next) => {
  if (req.query.token !== 'token-rahasia') {
    res.status(401).json({ pesan: 'Token tidak valid' });
    return;
  }
  next();
});

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

app.post('/rahasia/catatan', (req, res) => {
  res.status(201).json({ diterima: req.body });
});

// next('route'): lompat ke handler berikutnya dengan path sama.
app.get('/laporan', (req, res, next) => {
  if (req.query.format !== 'ringkas') {
    next('route');
    return;
  }
  res.json({ format: 'ringkas' });
});
app.get('/laporan', (req, res) => res.json({ format: 'lengkap' }));

const server = app.listen(0, host, async () => {
  const base = `http://${host}:${server.address().port}`;
  const r1 = await fetch(`${base}/rahasia/catatan`);
  console.log('tanpa token :', r1.status, await r1.text());
  const r2 = await fetch(`${base}/rahasia/catatan?token=token-rahasia`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ isi: 'Catatan penting' }),
  });
  console.log('dengan token:', r2.status, await r2.text());
  const r3 = await fetch(`${base}/laporan`);
  console.log('laporan     :', r3.status, await r3.text());
  const r4 = await fetch(`${base}/laporan?format=ringkas`);
  console.log('ringkas     :', r4.status, await r4.text());
  console.log('log:', log.join(' | '));
  server.close();
  server.closeAllConnections();
});

Output yang Dihasilkan:

text
tanpa token : 401 {"pesan":"Token tidak valid"}
dengan token: 201 {"diterima":{"isi":"Catatan penting"}}
laporan     : 200 {"format":"lengkap"}
ringkas     : 200 {"format":"ringkas"}
log: GET /rahasia/catatan | POST /rahasia/catatan | GET /laporan | GET /laporan

Mari kita bedah middleware-middleware di dalamnya:

  1. Baris 8-11 (logger): middleware tanpa path berjalan untuk semua permintaan. Array log dikumpulkan, bukan langsung dicetak, supaya urutan keluaran uji tetap rapi; aplikasi nyata akan menulis ke berkas log atau layanan logging.
  2. Baris 14-20 (pemeriksa token): app.use('/rahasia', ...) hanya berlaku untuk path berawalan /rahasia. Ketika token tidak cocok, middleware mengirim 401 lewat res — perhatikan bahwa setelah mengirim respons, kode cukup return tanpa next(), sehingga pipeline berhenti.
  3. Baris 23 (express.json()): middleware bawaan yang mengisi req.body. Posisinya setelah pemeriksa token berarti body hanya diurai untuk permintaan yang lolos pemeriksaan.
  4. Baris 30-37 (next('route')): panggilan next('route') melompati sisa handler pada rute saat ini dan melanjutkan ke rute berikutnya yang path-nya sama. Inilah cara membuat satu rute dengan beberapa kandidat handler.

Menyajikan Berkas Statis #

express.static adalah middleware bawaan untuk menyajikan berkas dari sebuah folder. Kita siapkan folder publik di dalam bundle chapter ini berisi beranda.html dan data.txt, lalu mendaftarkannya sebagai middleware pertama:

js
import express from 'express';

const host = '127.0.0.1';
const app = express();

app.use(express.static('publik'));

app.use((req, res) => res.status(404).json({ pesan: 'Berkas tidak ditemukan' }));

const server = app.listen(0, host, async () => {
  const base = `http://${host}:${server.address().port}`;
  const r1 = await fetch(`${base}/beranda.html`);
  console.log('beranda :', r1.status, r1.headers.get('content-type'));
  console.log((await r1.text()).trim());
  const r2 = await fetch(`${base}/data.txt`);
  console.log('data    :', r2.status, r2.headers.get('content-type'), (await r2.text()).trim());
  const r3 = await fetch(`${base}/tak-ada.css`);
  console.log('tak ada :', r3.status, await r3.text());
  server.close();
  server.closeAllConnections();
});

Output yang Dihasilkan:

text
beranda : 200 text/html; charset=utf-8
<!DOCTYPE html><html lang="id"><head><meta charset="utf-8"><title>Aplikasi Tugas</title></head><body><h1>Selamat datang di beranda</h1><p>Berkas ini disajikan oleh express.static.</p></body></html>
data    : 200 text/plain; charset=utf-8 Budi,Siti,Dewi
tak ada : 404 {"pesan":"Berkas tidak ditemukan"}

Dua perilaku penting terlihat dari keluaran di atas. Pertama, header content-type diatur otomatis sesuai ekstensi berkas — text/html untuk .html dan text/plain untuk .txt — tanpa kita menulis pemetaan manual. Kedua, ketika berkas tidak ditemukan, express.static memanggil next() sehingga permintaan jatuh ke middleware 404 setelahnya; static tidak pernah mengirim 404 sendiri.

Middleware Populer di Ekosistem #

MiddlewareTugas
express.json()Mengubah body JSON menjadi req.body
express.urlencoded()Mengubah body form HTML menjadi req.body
express.static()Menyajikan berkas statis dari folder

Di luar tiga middleware bawaan tersebut, ekosistem Express menyediakan package middleware untuk hampir semua kebutuhan: morgan untuk logging terformat, helmet untuk mengatur header keamanan, dan cors untuk mengizinkan permintaan lintas domain. Ketiganya dipasang dengan pola yang sama — npm install lalu app.use(...) — sehingga kemampuan menulis middleware sendiri yang kita latih di chapter ini menjadi bekal untuk memakai middleware siap pakai.

Pipeline middleware mengurus permintaan yang berjalan mulus. Pertanyaan berikutnya: apa yang terjadi ketika handler melempar error di tengah pipeline? Express punya satu tempat khusus untuk menangkapnya, dan itulah materi Chapter Error Handling pada Express.

Referensi #