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