Cara Membuat Custom Middleware di Laravel

Cara Membuat Custom Middleware di Laravel


NEW
laravel middleware php

Cara membuat custom middleware di Laravel adalah salah satu skill penting yang wajib dikuasai setiap developer. Middleware bertindak sebagai “penjaga gerbang” yang memfilter HTTP request sebelum masuk ke controller. Dengan middleware, kamu bisa menangani autentikasi, logging, rate limiting, dan banyak lagi tanpa mengotori logic di controller.

Dalam tutorial ini, kita akan membahas cara membuat middleware dari nol, mendaftarkannya, hingga menerapkannya di route dan controller. Semua contoh menggunakan Laravel 11.

Apa Itu Middleware di Laravel?

Middleware adalah layer yang berada di antara HTTP request dan response. Setiap request yang masuk ke aplikasi Laravel akan melewati serangkaian middleware sebelum sampai ke controller.

Laravel sendiri sudah menyediakan beberapa middleware bawaan seperti auth, throttle, dan verified. Tapi dalam banyak kasus, kita perlu membuat middleware sendiri sesuai kebutuhan bisnis.

Beberapa use case umum custom middleware:

  • Mengecek role atau permission user
  • Mencatat log setiap request yang masuk
  • Memvalidasi API key dari header
  • Menambahkan header khusus ke response
  • Mengecek jam operasional aplikasi (maintenance window)

Membuat Middleware Pertama

Gunakan Artisan command untuk generate middleware baru:

php artisan make:middleware CekRoleAdmin

File baru akan muncul di app/Http/Middleware/CekRoleAdmin.php. Berikut isi default-nya:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CekRoleAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }
}

Method handle() adalah inti dari middleware. Di sinilah kita menulis logic filtering. Parameter $next adalah callback yang meneruskan request ke layer berikutnya.

Contoh 1: Middleware Cek Role Admin

Kasus paling umum adalah membatasi akses halaman berdasarkan role user. Berikut implementasinya:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CekRoleAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        if (!auth()->check() || auth()->user()->role !== 'admin') {
            abort(403, 'Akses ditolak. Anda bukan admin.');
        }

        return $next($request);
    }
}

Logic-nya sederhana: jika user belum login atau role-nya bukan admin, kembalikan response 403. Jika lolos, request diteruskan ke controller.

Middleware dengan Parameter

Bagaimana jika kita ingin middleware yang lebih fleksibel, bisa menerima role apa saja? Gunakan parameter:

class CekRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        if (!auth()->check() || !in_array(auth()->user()->role, $roles)) {
            abort(403, 'Akses ditolak.');
        }

        return $next($request);
    }
}

Penggunaannya di route:

Route::get('/dashboard', DashboardController::class)
    ->middleware('cek.role:admin,manager');

Dengan cara ini, satu middleware bisa dipakai untuk berbagai kombinasi role.

Mendaftarkan Middleware

Di Laravel 11, pendaftaran middleware dilakukan di bootstrap/app.php:

use App\Http\Middleware\CekRole;

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'cek.role' => CekRole::class,
    ]);
})

Setelah didaftarkan dengan alias, kamu bisa menggunakannya di route atau controller.

Tiga Cara Mendaftarkan Middleware

  1. Alias - untuk middleware yang dipakai per-route (seperti contoh di atas)
  2. Global - dijalankan untuk semua request tanpa kecuali
  3. Group - ditambahkan ke group web atau api
->withMiddleware(function (Middleware $middleware) {
    // Global middleware
    $middleware->append(LogSemuaRequest::class);

    // Tambah ke group api
    $middleware->api(append: [
        ValidasiApiKey::class,
    ]);

    // Alias
    $middleware->alias([
        'cek.role' => CekRole::class,
    ]);
})

Contoh 2: Middleware Logging Request

Middleware ini mencatat setiap request yang masuk ke file log. Sangat berguna untuk debugging dan audit trail:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class LogRequest
{
    public function handle(Request $request, Closure $next): Response
    {
        $start = microtime(true);

        $response = $next($request);

        $duration = round((microtime(true) - $start) * 1000, 2);

        Log::info('HTTP Request', [
            'method'   => $request->method(),
            'url'      => $request->fullUrl(),
            'ip'       => $request->ip(),
            'user_id'  => auth()->id(),
            'status'   => $response->getStatusCode(),
            'duration' => "{$duration}ms",
        ]);

        return $response;
    }
}

Perhatikan bahwa kita memanggil $next($request) terlebih dahulu, baru mencatat log setelah response didapat. Ini disebut after middleware karena logic-nya berjalan setelah request diproses controller.

Before vs After Middleware

Perbedaan kunci yang perlu dipahami:

// Before middleware - logic SEBELUM controller
public function handle($request, Closure $next)
{
    // Logic di sini jalan SEBELUM controller
    $this->validasi($request);

    return $next($request);
}

// After middleware - logic SETELAH controller
public function handle($request, Closure $next)
{
    $response = $next($request);

    // Logic di sini jalan SETELAH controller
    $this->catatLog($response);

    return $response;
}

Before middleware cocok untuk validasi dan pengecekan akses. After middleware cocok untuk logging, modifikasi response, atau menambahkan header.

Contoh 3: Middleware Validasi API Key

Untuk API yang butuh autentikasi via header, middleware ini sangat praktis:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class ValidasiApiKey
{
    public function handle(Request $request, Closure $next): Response
    {
        $apiKey = $request->header('X-API-Key');

        if (!$apiKey || $apiKey !== config('services.internal.api_key')) {
            return response()->json([
                'error' => 'API key tidak valid',
            ], 401);
        }

        return $next($request);
    }
}

Daftarkan ke group api agar semua route API otomatis terproteksi:

$middleware->api(append: [
    ValidasiApiKey::class,
]);

Contoh 4: Middleware Rate Limiting Custom

Laravel sudah punya throttle, tapi kadang kita butuh logic rate limiting yang lebih spesifik. Misalnya, limit berbeda untuk user free dan premium:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Symfony\Component\HttpFoundation\Response;

class CustomRateLimit
{
    public function handle(Request $request, Closure $next): Response
    {
        $user = auth()->user();
        $key = 'api:' . ($user?->id ?? $request->ip());

        $maxAttempts = $user?->is_premium ? 100 : 20;

        if (RateLimiter::tooManyAttempts($key, $maxAttempts)) {
            $retryAfter = RateLimiter::availableIn($key);

            return response()->json([
                'error' => 'Terlalu banyak request',
                'retry_after' => $retryAfter,
            ], 429);
        }

        RateLimiter::hit($key, 60);

        return $next($request);
    }
}

User premium mendapat limit 100 request per menit, sedangkan user free hanya 20. Jauh lebih fleksibel dibanding throttle bawaan.

Tips dan Best Practices

1. Jaga Middleware Tetap Ringan

Middleware dijalankan di setiap request. Hindari query database yang berat atau proses yang memakan waktu lama. Jika perlu data user, manfaatkan cache:

$user = Cache::remember("user:{$userId}", 300, function () use ($userId) {
    return User::find($userId);
});

2. Gunakan Terminable Middleware untuk Tugas Berat

Jika ada tugas yang tidak perlu selesai sebelum response dikirim (misalnya logging ke external service), gunakan method terminate():

class LogKeExternal
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    public function terminate($request, $response)
    {
        // Jalan SETELAH response dikirim ke browser
        ExternalLogger::send($request, $response);
    }
}

3. Testing Middleware

Pastikan middleware kamu berjalan dengan benar menggunakan test:

public function test_middleware_tolak_non_admin(): void
{
    $user = User::factory()->create(['role' => 'user']);

    $response = $this->actingAs($user)->get('/admin/dashboard');

    $response->assertStatus(403);
}

public function test_middleware_izinkan_admin(): void
{
    $admin = User::factory()->create(['role' => 'admin']);

    $response = $this->actingAs($admin)->get('/admin/dashboard');

    $response->assertStatus(200);
}

4. Urutan Middleware Penting

Middleware dieksekusi sesuai urutan pendaftaran. Pastikan middleware autentikasi jalan sebelum middleware cek role. Salah urutan bisa menyebabkan error karena auth()->user() bernilai null.

Kesimpulan

Custom middleware di Laravel memberikan kontrol penuh atas alur HTTP request di aplikasi kamu. Mulai dari cek role sederhana sampai rate limiting yang kompleks, semuanya bisa ditangani dengan elegan menggunakan middleware.

Kunci utamanya: jaga middleware tetap fokus pada satu tanggung jawab, buat ringan, dan selalu tulis test. Dengan pendekatan ini, kode kamu akan lebih bersih, maintainable, dan mudah di-debug.

Selamat mencoba dan semoga tutorial ini membantu workflow development Laravel kamu!

© 2026 D.N. Zaki Zamani