0

Route registration — Route::get/post/put/patch/delete/any

Beginner5 min read·lv-06-001

Concept

Laravel's router is the front door to your application. When public/index.php boots the HTTP kernel, it calls $kernel->handle($request), which dispatches the request through the middleware stack and ultimately hands it to Illuminate\Routing\Router. Routes are registered in routes/web.php and routes/api.php by Illuminate\Foundation\Support\Providers\RouteServiceProvider, which calls Route::middleware()->group() to load each file inside a group context.

The Route facade proxies to Illuminate\Routing\Router, which stores every registered route inside an Illuminate\Routing\RouteCollection. When you call Route::get('/users', [UserController::class, 'index']), the Router creates an Illuminate\Routing\Route object — not a plain array — and adds it to the collection keyed by HTTP method and URI pattern. The Route object holds the URI, methods, action (controller/closure), middleware stack, and compiled regex for later matching.

HTTP verbs map to dedicated router methods: get, post, put, patch, delete, options. Route::any() registers all verbs simultaneously. Route::match(['get', 'post'], '/login', ...) registers exactly the listed verbs. Under the hood every one of these calls addRoute(array $methods, string $uri, mixed $action) on the Router, which creates the Route instance and registers it in the RouteCollection.

A crucial real-world pattern is separating route files by concern. Laravel ships with web.php (cookie/session middleware group) and api.php (stateless, prefixed /api, uses auth:sanctum or similar). You can add more files in RouteServiceProvider::boot(). Large teams often split by domain — routes/admin.php, routes/billing.php — each loaded inside its own group so prefixes and middleware stay isolated without polluting the root collection.

A gotcha: route order matters. Laravel matches routes in registration order, so a literal route /users/export must be registered before /users/{id} or the dynamic segment will consume the literal string export. This is less of an issue with RouteCollection's lookup because it prefers static routes over parameterized ones internally, but explicit ordering avoids surprises.

Code Example

php
<?php

use App\Http\Controllers\UserController;
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;

// Basic verb-specific registration
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
Route::put('/users/{id}', [UserController::class, 'update']);
Route::patch('/users/{id}', [UserController::class, 'patch']);
Route::delete('/users/{id}', [UserController::class, 'destroy']);

// Match multiple verbs
Route::match(['get', 'post'], '/login', [AuthController::class, 'login']);

// Catch all verbs
Route::any('/webhook', [WebhookController::class, 'handle']);

// Closure route — fine for quick prototypes, avoid in production
Route::get('/ping', function () {
    return response()->json(['status' => 'ok']);
});

// Route::view shorthand for static pages (no controller needed)
Route::view('/about', 'pages.about');

// Route::redirect for permanent moves
Route::redirect('/old-path', '/new-path', 301);

Interview Q&A

Q: What class actually stores registered routes and how does Laravel look up a matching route at request time?

Routes are stored in Illuminate\Routing\RouteCollection (or CompiledRouteCollection when cached). At dispatch time, Router::findRoute() iterates the collection and calls Route::matches($request) on each candidate. Route::matches() compiles the URI pattern to a regex using Illuminate\Routing\CompiledRoute (backed by Symfony's RouteCompiler) and checks the HTTP method, host constraints, scheme, and compiled pattern against the live request. The first Route that passes all checks is returned and its action executed.


Q: Why can't you use Route::get() inside a service provider's register() method?

The register() phase runs before the application is fully booted — the RouteServiceProvider hasn't fired yet, so the router's middleware groups and route files haven't been loaded. Registering a route in register() would place it outside the web/api middleware groups, miss prefix configuration, and potentially conflict with route caching. Always register routes in boot() of a service provider, or inside RouteServiceProvider::boot() itself.


Q: What is the difference between Route::any() and Route::match()?

Route::any() registers the route for all eight HTTP methods that Laravel supports: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS. Route::match() accepts an explicit array of methods, so you control exactly which verbs are active. Use match when a route should accept both GET (display form) and POST (submit form) but not DELETE or PUT, keeping your surface area intentional and your API semantically correct.