Setiap request yang masuk ke aplikasi Laravel melewati perjalanan panjang sebelum controller akhirnya berjalan, dan perjalanan kembali ke pengguna juga tidak langsung. Middleware adalah lapisan-lapisan kode yang berdiri di tengah perjalanan itu: menyaring request sebelum diserahkan ke controller, dan menyentuh response sebelum dikirim keluar. Kebutuhan seperti memeriksa login, mencatat aktivitas, atau membatasi jumlah request bukan milik satu controller saja, melainkan kebutuhan lintas route. Dengan middleware, kita mengeluarkan logika lintas itu dari controller agar tidak diulang-ulang di banyak tempat.

Middleware sebagai Lapisan Bawang #

Bayangkan aplikasi sebagai inti bawang dan middleware sebagai kulit-kulit yang membungkusnya. Request menembus kulit dari luar ke dalam, controller memproses, lalu response menembus balik dari dalam ke luar. Middleware yang ditulis paling awal menjadi kulit terluar, sehingga kode before dan after middleware tersebut mengapit seluruh proses di dalamnya. Gambar berikut memperlihatkan bentuk umumnya:

Kita bisa membuktikan pola ini dengan dua middleware kecil bernama FirstLayer dan SecondLayer. Setiap lapisan menulis jejak (trace) sebelum dan sesudah memanggil $next($request), dan sebuah route menuliskan jejak controller di tengah:

php
// app/Http/Middleware/FirstLayer.php (potongan inti)
public function handle(Request $request, Closure $next): Response
{
    TraceLog::add('Lapisan 1: sebelum');

    $response = $next($request);

    TraceLog::add('Lapisan 1: sesudah');

    return $response;
}

// routes/web.php
Route::get('/lapisan', function () {
    TraceLog::add('Controller: memproses');

    return 'Isi controller';
})->middleware([FirstLayer::class, SecondLayer::class]);

Feature test berikut memanggil route tersebut lalu mencetak seluruh jejak yang terkumpul:

php
public function test_urutan_lapisan_mengikuti_urutan_deklarasi(): void
{
    TraceLog::$entries = [];

    $response = $this->get('/lapisan');

    echo 'trace: ', implode(' > ', TraceLog::$entries), PHP_EOL;
    echo 'isi: ', $response->getContent(), PHP_EOL;
}

Output yang Dihasilkan:

text
trace: Lapisan 1: sebelum > Lapisan 2: sebelum > Controller: memproses > Lapisan 2: sesudah > Lapisan 1: sesudah
isi: Isi controller

Perhatikan simetrinya: FirstLayer masuk paling awal dan keluar paling akhir. Middleware yang ditulis lebih dulu di daftar middleware() selalu membungkus semua middleware di dalamnya.

Logika Before dan After #

Kedua posisi middleware punya tugas berbeda. Logika before berjalan sebelum $next($request) dipanggil — tempat yang tepat untuk memvalidasi request, memeriksa session, atau menolak akses lebih awal. Jika kondisi tidak terpenuhi, middleware boleh mengembalikan response sendiri tanpa memanggil $next($request) sama sekali, sehingga controller tidak pernah dieksekusi. Logika after berjalan setelah $next($request) mengembalikan response — tempat untuk menambah header, mengubah isi, atau mencatat metrik sebelum response berangkat ke pengguna. Satu middleware boleh memakai keduanya sekaligus, seperti contoh FirstLayer di atas.

Pipeline: Mesin di Balik Lapisan #

Secara internal, Laravel membangun susunan bawang ini dengan komponen Pipeline. Pipeline menerima request, mengalirkannya melewati setiap middleware, lalu mengeksekusi inti di ujungnya. Potongan berikut meniru mekanisme itu dengan dua closure dan satu inti sederhana:

php
use Illuminate\Http\Request;
use Illuminate\Pipeline\Pipeline;

$request = Request::create('/contoh', 'GET');

$hasil = (new Pipeline($app))
    ->send($request)
    ->through([
        function ($request, $next) {
            echo "lapisan A: sebelum\n";
            $response = $next($request);
            echo "lapisan A: sesudah\n";
            return $response;
        },
        function ($request, $next) {
            echo "lapisan B: sebelum\n";
            $response = $next($request);
            echo "lapisan B: sesudah\n";
            return $response;
        },
    ])
    ->then(function ($request) {
        echo "inti: controller berjalan\n";
        return 'Respons dari controller';
    });

Output yang Dihasilkan:

text
lapisan A: sebelum
lapisan B: sebelum
inti: controller berjalan
lapisan B: sesudah
lapisan A: sesudah
hasil: 'Respons dari controller'

Setiap middleware pada dasarnya adalah Closure($request, $next) yang memutuskan: meneruskan request ke lapisan berikutnya, atau menghentikannya. Kita akan jarang menyentuh Pipeline secara langsung, tetapi memahaminya menjelaskan mengapa urutan middleware berpengaruh besar. Detail posisi middleware dalam keseluruhan perjalanan request dibahas di dokumentasi lifecycle .

Apa yang Cocok Dikerjakan Middleware #

Middleware paling tepat untuk kebutuhan yang berlaku umum bagi banyak route, bukan logika bisnis milik satu halaman:

KebutuhanContoh bawaan LaravelYang dilakukan
Autentikasiauth, guestMenahan tamu atau pengguna yang salah arah
Pembatasan lajuthrottleMenolak request yang terlalu sering dari satu IP
Berbagi resource lintas originHandleCorsMenambah header CORS pada response
Kebersihan inputTrimStrings, ConvertEmptyStringsToNullMenormalkan data request masuk

Konsepnya sudah jelas, tugas berikutnya adalah menulis middleware pertama kita sendiri — lengkap dengan logika before, after, dan feature test yang membuktikan keduanya berjalan. Panduan resminya tersedia di dokumentasi middleware .