Originally published on laravel.learnio.dev — part of the Laravel best practices course. View this lesson on the original site.
Accept dynamic IDs without accepting garbage
Route parameters let one route handle many resource URLs, like /products/42 or /users/stuart.
Required parameter:
Route::get('/products/{id}', function (string $id) {
return "Product {$id}";
});
Optional parameter uses ? and a default value:
Route::get('/greet/{name?}', function (string $name = 'Guest') {
return "Hello {$name}";
});
Keep optional parameters at the end of the URI pattern.
Add constraints to avoid bad matches
Without constraints, Laravel may match unwanted input. Add explicit rules:
Route::get('/orders/{id}', function (string $id) {
return "Order {$id}";
})->whereNumber('id');
Common helpers:
whereNumber('id')whereAlpha('slug')whereAlphaNumeric('code')whereUuid('id')whereUlid('id')whereIn('status', ['open', 'closed'])
If a value does not match the constraint, Laravel returns a 404.
Multiple parameters with focused constraints
Route::get('/teams/{team}/users/{user}', function (string $team, string $user) {
return "{$team} - {$user}";
})->whereAlphaNumeric('team')->whereNumber('user');
This keeps your route table predictable and prevents accidental matches.
Bad way vs better way
Bad way (no constraints, accepts almost anything):
Route::get('/orders/{order}', [OrderController::class, 'show']);
Better way (explicit contract):
Route::get('/orders/{order}', [OrderController::class, 'show'])
->whereNumber('order')
->name('orders.show');
Why better:
- malformed values fail fast with 404,
- fewer surprises inside controller methods,
- route behaviour stays consistent across the team.
Controller example
use App\Http\Controllers\OrderController;
Route::get('/orders/{order}', [OrderController::class, 'show'])
->whereNumber('order')
->name('orders.show');
In OrderController@show, Laravel passes the parameter value to your method argument.
Parameter matching flow
flowchart LR
A[Incoming URL] --> B[Route pattern with parameters]
B --> C[Constraint check]
C --> D[Controller or closure]
C --> E[404 if invalid]
Next, you will use these parameterised routes with named routes and URL generation to keep links stable as your app grows.
From Laravel best practices — Routing, Controllers & Request Flow