Kalau kamu pernah debugging Laravel dan bingung “ini kode dijalankan dari mana? Siapa yang manggil ini duluan?” — itu pertanda kamu belum punya gambaran besar tentang lifecycle-nya.
Artikel ini akan tunjukkan perjalanan lengkap sebuah HTTP request: dari detik pertama browser ngirim request, sampai detik terakhir Laravel ngirim response balik. Bukan hanya teorinya — kita akan lihat file-file nyata yang terlibat dan apa yang mereka lakukan.
Analoginya satu dulu, baru kita ikutin perjalanannya tahap demi tahap.
🏨 Analogi Besar — Hotel
Bayangin sebuah hotel bintang lima.
Tamu datang → disambut pintu utama → dicek di resepsionis → diantarkan lewat koridor tertentu → sampai di kamar yang sesuai → pelayanan dilakukan → tamu puas dan pulang.
Di balik layar, ada manajer hotel yang sudah menyiapkan semua fasilitas sebelum hotel buka: lampu dinyalakan, staf ditempatkan, SOP dijalankan.
Request di Laravel persis seperti itu:
Browser (tamu)
↓
public/index.php (pintu utama)
↓
bootstrap/app.php (manajer hotel)
↓
HTTP Kernel (resepsionis)
↓
Middleware (pemeriksaan di koridor)
↓
Router (penunjuk arah ke kamar)
↓
Controller → Service → Model (kamar + pelayanan)
↓
Response (tamu pulang)
Sekarang kita buka satu per satu.
1️⃣ public/index.php — Pintu Utama
Semua request HTTP yang masuk ke Laravel — tanpa terkecuali — melewati satu file ini. Tidak ada jalan lain.
📁 public/index.php
<?php
// Memuat autoloader Composer — supaya semua kelas PHP bisa ditemukan
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Cek apakah aplikasi sedang dalam maintenance mode
// Kalau iya, tampilkan halaman maintenance dan berhenti di sini
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
// Autoloader Composer — mendaftarkan semua kelas dari vendor/ dan app/
require __DIR__.'/../vendor/autoload.php';
// Bootstrap aplikasi — ini yang membangun "hotel"-nya
$app = require_once __DIR__.'/../bootstrap/app.php';
// Tangkap request HTTP yang datang dari browser
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Request::capture() // ← request dari browser ditangkap di sini
);
$response->send(); // ← response dikirim balik ke browser
$kernel->terminate($request, $response); // ← cleanup setelah response dikirim
📌 File ini pendek dan tidak pernah kamu sentuh — tapi dia adalah titik masuk tunggal untuk semua request. Web server (Nginx/Apache) dikonfigurasi untuk selalu mengarahkan semua request ke file ini.
2️⃣ bootstrap/app.php — Manajer Hotel
Setelah pintu dibuka, giliran manajer yang bekerja: menyiapkan semua fondasi aplikasi sebelum request diproses.
📁 bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
$app = Application::create(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php', // ← daftarkan file route web
api: __DIR__.'/../routes/api.php', // ← daftarkan file route API
commands: __DIR__.'/../routes/console.php',
)
->withMiddleware(function (Middleware $middleware) {
// ← konfigurasi middleware global di sini
})
->withExceptions(function (Exceptions $exceptions) {
// ← konfigurasi bagaimana exception ditangani
});
return $app;
📌 Di sinilah IoC Container (yang kita bahas di artikel DI) pertama kali hidup. Semua binding, service provider, dan konfigurasi dasar diinisialisasi di tahap ini. Kalau hotel adalah aplikasi, bootstrap/app.php adalah momen semua lampu dinyalakan dan semua staf disiapkan.
3️⃣ Service Provider — Staf yang Disiapkan Sebelum Buka
Sebelum request pertama masuk, Laravel menjalankan semua Service Provider yang terdaftar. Ini adalah tahap di mana semua “fasilitas hotel” disiapkan.
Setiap Service Provider punya dua method:
register() → daftarkan binding ke IoC Container
boot() → jalankan sesuatu setelah semua provider selesai register
📁 app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use App\Contracts\CommentRepositoryInterface;
use App\Repositories\EloquentCommentRepository;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
// Tahap ini: daftarkan ke container
// "Kalau ada yang minta CommentRepositoryInterface, beri EloquentCommentRepository"
$this->app->bind(
CommentRepositoryInterface::class,
EloquentCommentRepository::class
);
}
public function boot(): void
{
// Tahap ini: semua provider sudah register, aman untuk akses service lain
// Contoh: daftarkan observer, gate, macro, view composer
\App\Models\Article::observe(\App\Observers\ArticleObserver::class);
}
}
📌 Urutan penting: semua register() dari semua provider dijalankan dulu, baru semua boot() dijalankan. Kenapa? Karena boot() boleh menggunakan binding yang didaftarkan provider lain — dan kalau urutannya terbalik, binding itu belum tentu ada.
4️⃣ HTTP Kernel — Resepsionis
Setelah aplikasi siap, request diserahkan ke HTTP Kernel. Ini adalah pusat komando yang menentukan apa saja yang harus dilalui sebuah request sebelum sampai ke controller.
📁 app/Http/Kernel.php (sebelum Laravel 11)
protected $middleware = [
// Middleware yang dijalankan untuk SEMUA request, tanpa terkecuali
\Illuminate\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
];
protected $middlewareGroups = [
// Middleware untuk request dari browser (cookie, session, CSRF)
'web' => [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
],
// Middleware untuk API (stateless, pakai token)
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
📌 Ada tiga lapis middleware di Kernel: global (semua request), group (web atau api), dan route-specific (hanya route tertentu). Request melewati ketiganya secara berurutan — seperti tamu yang harus lewat metal detector, lalu cek reservasi, lalu kunci kamar.
5️⃣ Middleware — Koridor Pemeriksaan
Middleware adalah kode yang berjalan sebelum dan/atau sesudah request sampai ke controller. Setiap middleware punya satu tanggung jawab spesifik.
Struktur middleware selalu sama:
public function handle(Request $request, Closure $next): Response
{
// Kode di sini → dijalankan SEBELUM request sampai ke controller
$response = $next($request); // ← request diteruskan ke middleware berikutnya
// Kode di sini → dijalankan SESUDAH controller selesai
return $response;
}
Contoh nyata — middleware untuk memastikan user sudah login sebelum bisa buat artikel:
📁 app/Http/Middleware/EnsureUserIsAuthor.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsAuthor
{
public function handle(Request $request, Closure $next): Response
{
// Cek sebelum request masuk ke controller
if (!$request->user() || $request->user()->role !== 'author') {
return response()->json(['message' => 'Unauthorized.'], 403);
// ← kalau tidak lolos, request BERHENTI di sini, tidak sampai controller
}
// Lolos pemeriksaan → teruskan ke middleware berikutnya atau controller
return $next($request);
}
}
📁 routes/api.php
Middleware dipasang ke route yang membutuhkannya.
Route::middleware(['auth:sanctum', EnsureUserIsAuthor::class])
->group(function () {
Route::post('/articles', [ArticleController::class, 'store']);
Route::put('/articles/{article}', [ArticleController::class, 'update']);
Route::delete('/articles/{article}', [ArticleController::class, 'destroy']);
});
📌 Bayangkan middleware seperti koridor dengan beberapa pos pemeriksaan. Request harus lolos semua pos secara berurutan. Kalau gagal di satu pos, dia dikembalikan langsung — tidak perlu repot sampai ke controller.
Urutan Middleware yang Sebenarnya
Ini yang sering tidak disadari: middleware berjalan seperti tumpukan (stack), bukan antrian lurus.
Middleware A masuk →
Middleware B masuk →
Middleware C masuk →
Controller
← Middleware C keluar
← Middleware B keluar
← Middleware A keluar
Yang pertama masuk adalah yang terakhir keluar. Jadi kalau kamu butuh middleware yang berjalan paling akhir saat response — daftarkan dia paling awal.
6️⃣ Router — Penunjuk Arah
Setelah lolos semua middleware global, request diserahkan ke Router. Router membaca URL dan HTTP method dari request, lalu mencocokkannya dengan daftar route yang sudah didaftarkan.
📁 routes/api.php
<?php
use App\Http\Controllers\ArticleController;
use App\Http\Controllers\CommentController;
// GET /api/articles → ArticleController@index
Route::get('/articles', [ArticleController::class, 'index']);
// GET /api/articles/1 → ArticleController@show, {article} di-resolve otomatis
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// POST /api/articles → butuh auth dulu
Route::middleware('auth:sanctum')->group(function () {
Route::post('/articles', [ArticleController::class, 'store']);
Route::post('/articles/{article}/comments', [CommentController::class, 'store']);
});
Route Model Binding — Fitur Yang Sering Tidak Disadari
Perhatikan {article} di route. Kalau parameter itu di-type-hint dengan Model di controller, Laravel otomatis mengambil data dari database — kamu tidak perlu Article::findOrFail($id) lagi.
📁 app/Http/Controllers/ArticleController.php
// Laravel otomatis query Article::findOrFail($article) sebelum method ini dipanggil
// Kalau tidak ketemu → otomatis 404
public function show(Article $article): JsonResponse
{
return response()->json($article->load('user', 'comments'));
}
📌 Ini namanya Route Model Binding — salah satu fitur Laravel yang paling menghemat kode. Cukup type-hint dengan Model yang sesuai nama parameter route, sisanya diurus otomatis.
7️⃣ Controller → Service → Model — Kamar dan Pelayanan
Request akhirnya sampai ke controller. Di sinilah logika aplikasi dijalankan — dan kalau kamu mengikuti SRP dari artikel SOLID, controller hanya bertugas sebagai penghubung antara HTTP dan logika bisnis.
📁 app/Http/Controllers/ArticleController.php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreArticleRequest;
use App\Models\Article;
use App\Services\ArticleService;
use Illuminate\Http\JsonResponse;
class ArticleController extends Controller
{
public function __construct(
private ArticleService $articleService // ← DI bekerja di sini
) {}
public function index(): JsonResponse
{
// Controller hanya meneruskan — tidak ada logika bisnis di sini
return response()->json(
$this->articleService->getPublished()
);
}
public function store(StoreArticleRequest $request): JsonResponse
{
// Form Request sudah validasi sebelum method ini dipanggil
$article = $this->articleService->create(
$request->validated(),
$request->user()
);
return response()->json($article, 201);
}
public function show(Article $article): JsonResponse
{
// Route Model Binding sudah resolve Article-nya
return response()->json($article->load('user', 'comments'));
}
}
📌 Perhatikan tiga hal yang sudah “diselesaikan” sebelum controller method dipanggil: (1) middleware sudah cek autentikasi, (2) Form Request sudah validasi input, (3) Route Model Binding sudah ambil data dari DB. Controller tinggal fokus pada logika bisnis level tinggi.
8️⃣ Response — Tamu Pulang
Setelah controller selesai, hasilnya dikembalikan sebagai Response object. Response ini lalu berjalan balik melewati semua middleware (dari dalam ke luar) sebelum akhirnya dikirim ke browser.
// Beberapa cara membuat response di Laravel:
// JSON response — paling umum untuk API
return response()->json($article, 201);
// Response dengan header tambahan
return response()->json($article)
->header('X-Article-Id', $article->id);
// Response dengan cookie
return response()->json($data)
->cookie('last_read', $article->id, 60);
Setelah $response->send() di public/index.php tadi, Laravel memanggil $kernel->terminate() — ini untuk menjalankan pekerjaan yang bisa ditunda: logging, cleanup, atau middleware yang butuh jalan setelah response dikirim (seperti mencatat waktu eksekusi).
🗺️ Gambaran Besar — Perjalanan Lengkap
Ini keseluruhan perjalanan dengan nama file aslinya:
Browser kirim: GET /api/articles/5
↓
public/index.php
→ autoload Composer
→ cek maintenance mode
→ bootstrap aplikasi
↓
bootstrap/app.php
→ bangun IoC Container
→ daftarkan route, middleware, exception handler
↓
Service Providers (semua register() dulu, baru boot())
→ AppServiceProvider, AuthServiceProvider, dll
↓
HTTP Kernel
→ jalankan global middleware
→ tentukan middleware group (web / api)
↓
Middleware Stack (masuk berurutan)
→ HandleCors → ThrottleRequests → auth:sanctum → EnsureUserIsAuthor
↓
Router
→ cocokkan GET /api/articles/5 → ArticleController@show
→ Route Model Binding: ambil Article::findOrFail(5) dari DB
↓
ArticleController@show(Article $article)
→ IoC Container inject ArticleService lewat constructor
→ return response()->json($article)
↓
Middleware Stack (keluar berurutan, kebalikan dari masuk)
→ EnsureUserIsAuthor ← auth:sanctum ← ThrottleRequests ← HandleCors
↓
public/index.php: $response->send()
→ HTTP response dikirim ke browser
↓
$kernel->terminate()
→ logging, cleanup
🧭 Urutan Eksekusi — Ringkasan
| # | Tahap | File | Yang Dilakukan |
|---|---|---|---|
| 1 | Entry point | public/index.php | Tangkap request, bootstrap app |
| 2 | Bootstrap | bootstrap/app.php | Bangun container, daftarkan konfigurasi |
| 3 | Service Provider | app/Providers/*.php | Siapkan binding dan fasilitas |
| 4 | HTTP Kernel | app/Http/Kernel.php | Tentukan middleware yang dijalankan |
| 5 | Middleware (masuk) | app/Http/Middleware/*.php | Auth, throttle, CSRF, dll |
| 6 | Router | routes/api.php | Cocokkan URL ke controller |
| 7 | Controller | app/Http/Controllers/*.php | Jalankan logika, buat response |
| 8 | Middleware (keluar) | app/Http/Middleware/*.php | Modifikasi response kalau perlu |
| 9 | Terminate | public/index.php | Kirim response, cleanup |
💡 Ingat!
Waktu pertama kali belajar ini, wajar kalau rasanya “ngapain harus tahu ini semua? Toh Laravel jalan sendiri.”
Jawabannya: karena begitu ada bug yang aneh, atau middleware yang tidak jalan sesuai harapan, atau exception yang muncul dari tempat yang tidak terduga — kamu butuh gambaran ini untuk tahu di mana harus mulai mencari.
Laravel memang bekerja dengan banyak “keajaiban” di balik layar. Tapi keajaiban itu punya urutan yang jelas dan konsisten. Setelah kamu tahu urutannya, debugging jadi jauh lebih terarah — dan kamu tidak lagi menebak-nebak.
