Backend10 min read8 views

Memahami Routing Laravel Konsep, Contoh Kode, & Best Practices

Pelajari sistem routing di Laravel secara mendalam. Mulai dari dasar, parameter, route groups, model binding, hingga optimasi produksi beserta contoh kodenya.

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:

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:

PHP
// 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::any sebaiknya 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.

PHP
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.

PHP
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.

PHP
// 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:

PHP
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:

PHP
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:

PHP
// 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():

PHP
// 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:

PHP
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:

PHP
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:

PHP
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:

PHP
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:

PHP
// 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:

PHP
// 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:

PHP
// 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:

PHP
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.

PHP
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:

PHP
// 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:

PHP
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:

Bash
php artisan make:controller DownloadInvoiceController --invokable

Di dalam berkas Controller:

PHP
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):

PHP
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.

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.

PHP
// 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:

Bash
php artisan route:cache

Hal Penting tentang Route Caching:

  1. Jangan Gunakan Closures di Production: route:cache tidak 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.

  2. Clear Cache Saat Deploy: Setiap kali kamu menambahkan atau mengubah rute baru di server production, kamu wajib menjalankan pembersihan cache terlebih dahulu:

Bash
# 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:

  1. Gunakan Named Routes: Selalu beri nama pada rute (->name()). Hindari hardcoding URL seperti <a href="/user/profile"> di tampilan HTML.

  2. Hindari Closure Terlalu Banyak: Pindahkan logika bisnis dari berkas routes/web.php ke dalam Controller. Biarkan berkas routing hanya bertugas membaca peta alamat URL.

  3. Manfaatkan Route Grouping: Kelompokkan rute yang memiliki Middleware, Prefix, atau Namespace yang sama agar penulisan kode tidak berulang (DRY - Don't Repeat Yourself).

  4. Optimalkan dengan Route Model Binding: Manfaatkan fitur ini untuk mengurangi penulisan kode query database findOrFail() yang repetitif.

  5. Pisahkan Rute Web dan API: Selalu letakkan endpoint JSON/API di routes/api.php dan tampilan web HTML di routes/web.php untuk memisahkan mekanisme keamanan (Session CSRF vs Token Auth).