Menulis kode Laravel tanpa memahami routing ibarat membangun rumah tanpa pintu dan koridor. Kamu bisa saja membuat pondasi database yang kokoh dan tampilan UI Blade/React yang luar biasa, tetapi tanpa routing, aplikasi web tidak akan pernah tahu bagaimana menghubungkan permintaan pengguna (request) ke logika bisnis yang tepat.
Routing adalah jantung dari alur data aplikasi Laravel. Di artikel ini, kita akan mengupas tuntas konsep routing di Laravel dari level paling dasar hingga teknik tingkat lanjut (advanced) yang biasa digunakan di aplikasi skala produksi.
1. Fondasi Routing di Laravel
Secara sederhana, routing bertugas menerima HTTP Request (seperti klik tombol, submit form, atau pemanggilan API) dan mengarahkannya ke eksekusi kode yang sesuai—baik itu berupa Closure (fungsi anonim) maupun Controller.
Struktur Berkas Routing
Sejak Laravel versi 11, struktur berkas routing dibuat lebih efisien. Namun secara umum, Laravel membagi sistem routing ke dalam beberapa berkas utama di dalam direktori routes/:
-
routes/web.php: Digunakan untuk rute halaman web konvensional. Rute di sini memiliki status (stateful), menggunakan sesi (session), perlindungan CSRF (CSRF protection), dan enkripsi cookie. -
routes/api.php: Digunakan untuk layanan REST API. Rute ini secara otomatis bersifat stateless dan biasanya diproteksi menggunakan token (seperti Laravel Sanctum atau Passport) serta dikenakan pembatasan akses (rate limiting). -
routes/console.php: Berisi perintah berbasis CLI (Artisan Commands). -
routes/channels.php: Digunakan untuk mendaftarkan channel event broadcasting (WebSockets).
2. Sintaks Dasar Dan HTTP Verbs
Laravel mendukung semua HTTP Verbs standar. Berikut adalah contoh penulisan rute paling dasar di routes/web.php:
use Illuminate\Support\Facades\Route;
// Rute HTTP GET
Route::get('/tentang-kami', function () {
return view('about');
});
// Rute HTTP POST
Route::post('/kontak', function () {
// Logika untuk menangani pengiriman form kontak
});
// Rute HTTP PUT (Biasanya untuk memperbarui seluruh data)
Route::put('/artikel/{id}', function ($id) {
// Logika update artikel
});
// Rute HTTP PATCH (Untuk memperbarui sebagian data)
Route::patch('/user/{id}/status', function ($id) {
// Logika update status user
});
// Rute HTTP DELETE (Untuk menghapus data)
Route::delete('/artikel/{id}', function ($id) {
// Logika hapus artikel
});
Penanganan Rute Multi-Method
Terkadang kamu membutuhkan satu rute yang mampu menerima beberapa jenis HTTP verb sekaligus:
// Menerima hanya method GET dan POST
Route::match(['get', 'post'], '/feedback', function () {
// ...
});
// Menerima semua HTTP Verbs (GET, POST, PUT, DELETE, dll)
Route::any('/webhook/stripe', function () {
// ...
});
Catatan Developer: Penggunaan
Route::anysebaiknya dibatasi hanya untuk keperluan khusus seperti integrasi webhook pihak ketiga. Menggunakannya secara sembarangan bisa membuka celah kelemahan keamanan pada aplikasi.
3. Menangani Route Parameters
Aplikasi web modern hampir selalu memerlukan nilai dinamis pada URL, seperti ID produk, slug artikel, atau nama pengguna.
a. Required Parameters (Parameter Wajib)
Parameter ditandai dengan kurung kurawal {} di dalam URI rute. Nilai dari URL akan disuntikkan secara berurutan ke dalam fungsi callback atau controller.
Route::get('/produk/{id}', function (string $id) {
return "Menampilkan detail produk dengan ID: " . $id;
});
// Rute dengan beberapa parameter
Route::get('/kategori/{category}/produk/{productId}', function ($category, $productId) {
return "Kategori: {$category} | Produk ID: {$productId}";
});
b. Optional Parameters (Parameter Opsional)
Jika suatu parameter tidak wajib diisi pada URL, tambahkan tanda tanya ? di akhir nama parameter dan berikan nilai default pada variabel fungsi.
Route::get('/laporan/{bulan?}', function (?string $bulan = null) {
if (!$bulan) {
$bulan = date('m');
}
return "Menampilkan laporan bulan: " . $bulan;
});
c. Parameter Constraints (Validasi RegEx)
Untuk mencegah pengguna memasukkan format URL yang salah (misalnya memasukkan teks abc pada rute yang membutuhkan angka ID), kamu bisa membatasi format parameter menggunakan metode where.
// Memastikan parameter ID hanya berupa angka
Route::get('/user/{id}', function (string $id) {
return "User ID: " . $id;
})->where('id', '[0-9]+');
// Memastikan nama hanya berupa huruf alfabet
Route::get('/profile/{username}', function (string $username) {
return "Profil: " . $username;
})->where('username', '[A-Za-z]+');
// Validasi banyak parameter sekaligus
Route::get('/post/{id}/{slug}', function ($id, $slug) {
// ...
})->where([
'id' => '[0-9]+',
'slug' => '[a-z0-9-]+'
]);
Laravel juga menyediakan helper methods ringkas untuk pola validasi yang sering dipakai:
Route::get('/user/{id}', function ($id) { ... })->whereNumber('id');
Route::get('/category/{name}', function ($name) { ... })->whereAlpha('name');
Route::get('/post/{slug}', function ($slug) { ... })->whereAlphaNumeric('slug');
Route::get('/order/{uuid}', function ($uuid) { ... })->whereUuid('uuid');
4. Named Routes (Memberi Nama pada Rute)
Named Routes mempermudah pembuatan URL atau redirect tanpa harus menulis ulang URI secara manual (hardcode). Jika suatu hari kamu mengubah struktur URI, kamu tidak perlu mengubah URL satu per satu di Blade view atau Controller.
Cara Mendaftarkan Nama Rute
Gunakan metode name() di akhir deklarasi rute:
Route::get('/user/profile/settings', [UserProfileController::class, 'show'])
->name('profile.settings');
Cara Menggunakan Named Route
Kamu bisa memanggil rute ini di dalam kode PHP maupun file view Blade:
// Generating URL dari nama rute
$url = route('profile.settings');
// Mengarahkan (Redirect) pengguna ke nama rute
return redirect()->route('profile.settings');
Jika rute memiliki parameter, passing parameternya sebagai argumen kedua pada fungsi route():
// Definisi Rute
Route::get('/artikel/{slug}', [PostController::class, 'show'])->name('posts.show');
// Penggunaan di Blade View
// Output HTML: <a href="http://domain.com/artikel/panduan-laravel">Baca Artikel</a>
<a href="{{ route('posts.show', ['slug' => 'panduan-laravel']) }}">Baca Artikel</a>
5. Route Groups Dan Middleware
Saat aplikasi mulai membesar, menulis rute satu per satu akan membuat berkas web.php menjadi sangat panjang dan sulit dirawat. Di sinilah Route Groups digunakan untuk mengelompokkan rute yang memiliki kesamaan atribut seperti middleware, prefix, name, atau domain.
┌───────────────────────────┐
│ Route::group() │
└─────────────┬─────────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Middleware │ │ Prefix Path │ │ Name Prefix │
│ auth, verified │ │ /admin │ │ admin. │
└──────────────────┘ └──────────────────┘ └──────────────────┘
a. Grouping Berdasarkan Middleware
Jika ada beberapa rute yang hanya boleh diakses oleh pengguna yang sudah login, bungkus rute tersebut dengan middleware auth:
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
});
b. Grouping Berdasarkan Path Prefix
Jika semua rute dalam satu kelompok diawali dengan kata tertentu (misalnya /admin), gunakan prefix:
Route::prefix('admin')->group(function () {
// Mengakses URL: /admin/users
Route::get('/users', [AdminController::class, 'users']);
// Mengakses URL: /admin/settings
Route::get('/settings', [AdminController::class, 'settings']);
});
c. Grouping Berdasarkan Route Name Prefix
Menggabungkan awalan nama rute agar penamaan lebih terstruktur:
Route::name('admin.')->prefix('admin')->group(function () {
// Nama rute menjadi: admin.dashboard
Route::get('/dashboard', [AdminController::class, 'index'])->name('dashboard');
// Nama rute menjadi: admin.reports
Route::get('/reports', [AdminController::class, 'reports'])->name('reports');
});
Kombinasi Lengkap: Struktur Rute Panel Admin Realistis
Berikut adalah pola pengelompokan lengkap yang biasa diterapkan dalam proyek aplikasi nyata:
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\UserController;
Route::middleware(['auth', 'role:admin'])
->prefix('admin')
->name('admin.')
->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
// Grouping bertingkat (Nested Group)
Route::prefix('users')->name('users.')->group(function () {
Route::get('/', [UserController::class, 'index'])->name('index');
Route::get('/create', [UserController::class, 'create'])->name('create');
Route::post('/', [UserController::class, 'store'])->name('store');
Route::delete('/{user}', [UserController::class, 'destroy'])->name('destroy');
});
});
6. Route Model Binding
Salah satu fitur paling efisien di Laravel adalah Route Model Binding. Fitur ini secara otomatis menginjeksikan instance Eloquent Model langsung ke dalam method Controller berdasarkan nilai ID atau key yang dipassing dari URL.
Implicit Binding (Binding Implisit)
Alih-alih mengambil ID lalu melakukan query User::findOrFail($id) secara manual, Laravel melakukan proses pencarian ini secara otomatis di belakang layar.
Tanpa Route Model Binding:
// Rute: /user/5
Route::get('/user/{id}', function ($id) {
$user = App\Models\User::findOrFail($id);
return view('user.profile', ['user' => $user]);
});
Menggunakan Route Model Binding:
// Nama parameter {user} HARUS SAMA dengan nama variabel $user di callback/controller
Route::get('/user/{user}', function (App\Models\User $user) {
return view('user.profile', ['user' => $user]);
});
Jika ID 5 tidak ditemukan di database, Laravel akan otomatis mengembalikan respon 404 Not Found.
Kustomisasi Column Key (Custom Keys)
Secara default, Route Model Binding menggunakan kolom id. Jika kamu ingin mencari record berdasarkan kolom lain (misalnya slug), tentukan nama kolomnya di rute:
// Mencari data berdasarkan kolom 'slug', bukan 'id'
Route::get('/posts/{post:slug}', function (App\Models\Post $post) {
return view('posts.show', ['post' => $post]);
});
Atau ubah secara global di dalam Model Eloquent kamu:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* Mengubah default route key binding untuk model ini.
*/
public function getRouteKeyName(): string
{
return 'slug';
}
}
7. Resource Dan API Controllers
Laravel menyediakan konvensi pembuatan rute untuk operasi CRUD (Create, Read, Update, Delete) secara efisien hanya dengan satu baris kode.
Resource Controller
Dengan Route::resource, Laravel secara otomatis mendaftarkan 7 rute standar RESTful yang terhubung ke method di Controller.
use App\Http\Controllers\ArticleController;
Route::resource('articles', ArticleController::class);
Tabel pemetaan rute yang dihasilkan oleh Route::resource:
| Verb HTTP | URI Path | Method Controller | Nama Rute (route()) | Fungsi/Tujuan |
| GET | /articles |
index() |
articles.index |
Menampilkan daftar artikel |
| GET | /articles/create |
create() |
articles.create |
Menampilkan form tambah artikel |
| POST | /articles |
store() |
articles.store |
Menyimpan data artikel baru |
| GET | /articles/{article} |
show() |
articles.show |
Menampilkan detail satu artikel |
| GET | /articles/{article}/edit |
edit() |
articles.edit |
Menampilkan form edit artikel |
| PUT/PATCH | /articles/{article} |
update() |
articles.update |
Memperbarui data artikel |
| DELETE | /articles/{article} |
destroy() |
articles.destroy |
Menghapus artikel |
Membatasi Resource Routes
Jika kamu tidak membutuhkan seluruh 7 method tersebut, kamu bisa membatasinya menggunakan only atau except:
// Hanya mengaktifkan index dan show
Route::resource('photos', PhotoController::class)->only([
'index', 'show'
]);
// Mengaktifkan semua KECUALI destroy
Route::resource('roles', RoleController::class)->except([
'destroy'
]);
API Resource Controller
Saat membuat API backend, kamu tidak memerlukan method yang mengembalikan HTML view seperti create() dan edit(). Gunakan apiResource untuk secara otomatis mengecualikan kedua rute tersebut:
use App\Http\Controllers\Api\ProductController;
// Hanya mendaftarkan 5 rute REST API (tanpa create & edit)
Route::apiResource('products', ProductController::class);
8. Single Action Controllers (__invoke)
Jika sebuah controller hanya menangani satu tugas spesifik (misalnya me-generate laporan PDF atau memproses checkout), kamu tidak perlu membuat banyak method. Gunakan method magic __invoke.
Buat Controller lewat Artisan CLI:
php artisan make:controller DownloadInvoiceController --invokable
Di dalam berkas Controller:
namespace App\Http\Controllers;
use App\Models\Invoice;
class DownloadInvoiceController extends Controller
{
public function __invoke(Invoice $invoice)
{
// Logika pembuatan dan download PDF Invoice
return response()->download($path);
}
}
Mendaftarkan rute di routes/web.php (tanpa perlu menyebutkan nama method):
use App\Http\Controllers\DownloadInvoiceController;
Route::get('/invoice/{invoice}/download', DownloadInvoiceController::class)
->name('invoice.download');
9. Fallback Routes Dan Rate Limiting
Fallback Route (Halaman 404 Kustom)
Metode Route::fallback digunakan untuk menangkap semua permintaan URL yang tidak cocok dengan rute manapun yang terdaftar dalam aplikasi. Rute ini biasanya ditaruh di baris paling bawah berkas routes/web.php.
Route::fallback(function () {
return response()->view('errors.404-custom', [], 404);
});
Rate Limiting (Pembatasan Akses)
Untuk melindungi aplikasi dari serangan Brute Force atau spamming API, kamu bisa membatasi frekuensi akses URL menggunakan middleware throttle.
// Membatasi pengguna hanya bisa melakukan 60 kali request dalam 1 menit
Route::middleware('throttle:60,1')->group(function () {
Route::get('/api/search', [SearchController::class, 'query']);
});
// Pembatasan ketat untuk login: Maksimal 5 percobaan per menit
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:5,1');
10. Route Caching untuk Optimasi Performa Production
Saat aplikasi Laravel masuk ke lingkungan Production, Laravel harus membaca dan memproses seluruh berkas rute setiap kali ada permintaan HTTP yang masuk. Pada aplikasi skala besar dengan ratusan rute, proses parsing ini bisa menimbulkan sedikit overhead pada waktu eksekusi.
Kamu bisa mengkompilasi seluruh rute menjadi satu berkas cache cepat menggunakan perintah Artisan:
php artisan route:cache
Hal Penting tentang Route Caching:
-
Jangan Gunakan Closures di Production:
route:cachetidak mendukung penggunaan fungsi anonim (Closure) di dalam berkas rute jika kamu menggunakan Laravel versi lama. Praktik terbaik (best practice) adalah mengarahkan seluruh rute ke Controller class. -
Clear Cache Saat Deploy: Setiap kali kamu menambahkan atau mengubah rute baru di server production, kamu wajib menjalankan pembersihan cache terlebih dahulu:
# Menghapus cache rute
php artisan route:clear
# Menggenerasi ulang cache rute
php artisan route:cache
Ringkasan Dan Best Practices Penulisan Routing
Agar kode rute di proyek Laravel kamu tetap bersih (clean), mudah dibaca, dan mudah dikelola oleh tim, terapkan panduan berikut:
-
Gunakan Named Routes: Selalu beri nama pada rute (
->name()). Hindari hardcoding URL seperti<a href="/user/profile">di tampilan HTML. -
Hindari Closure Terlalu Banyak: Pindahkan logika bisnis dari berkas
routes/web.phpke dalam Controller. Biarkan berkas routing hanya bertugas membaca peta alamat URL. -
Manfaatkan Route Grouping: Kelompokkan rute yang memiliki Middleware, Prefix, atau Namespace yang sama agar penulisan kode tidak berulang (DRY - Don't Repeat Yourself).
-
Optimalkan dengan Route Model Binding: Manfaatkan fitur ini untuk mengurangi penulisan kode query database
findOrFail()yang repetitif. -
Pisahkan Rute Web dan API: Selalu letakkan endpoint JSON/API di
routes/api.phpdan tampilan web HTML diroutes/web.phpuntuk memisahkan mekanisme keamanan (Session CSRF vs Token Auth).