Cara Membuat Custom Middleware di Laravel
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
- Alias - untuk middleware yang dipakai per-route (seperti contoh di atas)
- Global - dijalankan untuk semua request tanpa kecuali
- Group - ditambahkan ke group
webatauapi
->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!