Laravel Routing Step by Step

·

·

Routing is the map between URLs and application logic. Laravel's expressive router lets you define clean URLs, attach middleware, and generate links from route names without hard-coding paths.

Basic Routes and HTTP Verbs

Laravel routes map URI patterns to closures or controller actions. The router supports GET, POST, PUT, PATCH, DELETE, and OPTIONS out of the box.

Route definitions in routes/web.php automatically receive the web middleware group, which enables sessions and CSRF protection for browser requests.

Step 1: Define GET and POST routes

A simple GET route returns HTML or JSON when a user visits a URL. Use Route::get('/posts', …) for listing and Route::post('/posts', …) for creating resources.

POST routes that accept form submissions must include @csrf in Blade forms or X-XSRF-TOKEN for SPA clients so Laravel's CSRF middleware accepts the request.

Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);

Step 2: Route parameters and constraints

Dynamic segments like /posts/{id} are passed to your controller method. Add where('id', '[0-9]+') or use whereNumber('id') to reject invalid IDs before they hit the database.

Optional parameters use {slug?} syntax with default values in the method signature. Laravel resolves parameters by name, so document your route signatures clearly.

Route::get('/posts/{id}', [PostController::class, 'show'])
    ->whereNumber('id');

Step 3: Redirect and view routes

Route::redirect('/home', '/dashboard') issues a 302 redirect without a controller. Route::view('/about', 'pages.about') returns a Blade view directly for static pages.

Use permanent redirects with Route::permanentRedirect when migrating URLs for SEO. View routes accept a second array argument to pass data to the template.

Route::view('/about', 'pages.about', ['team' => 'Aadyanex']);

Step 4: Fallback and rate limiting

Route::fallback registers a handler for undefined URLs, useful for custom 404 pages. Apply throttle middleware to sensitive endpoints like login or password reset.

In routes/api.php, the default api middleware group applies throttle:api using limits defined in RouteServiceProvider or bootstrap/app.php in Laravel 11.

Route::fallback(function () {
    return response()->view('errors.404', [], 404);
});

Named Routes and Route Groups

Named routes let you generate URLs with route('posts.show', $post) instead of concatenating strings. Refactoring paths becomes safe because Blade and controllers reference names.

Route groups apply shared attributes—prefixes, middleware, and namespaces—to many routes at once, keeping route files readable as applications scale.

Step 1: Name your routes

Chain ->name('posts.index') on each route or use name() on a group. In Blade, use href="{{ route('posts.show', $post) }}" for type-safe links.

Route names should follow resource conventions: posts.index, posts.create, posts.store, posts.show, posts.edit, posts.update, posts.destroy. This matches php artisan route:resource output.

Route::get('/posts/{post}', [PostController::class, 'show'])
    ->name('posts.show');

Step 2: Prefix and name groups

Wrap admin routes in Route::prefix('admin')->name('admin.')->group(…) so URLs become /admin/users and names become admin.users.index.

Nested groups inherit parent attributes. Combine prefix, middleware, and controller groups to avoid repeating [AdminController::class, …] on every line.

Route::prefix('admin')->name('admin.')->middleware('auth')->group(function () {
    Route::resource('users', AdminUserController::class);
});

Step 3: Route model binding

Type-hint Post $post in your controller and Laravel resolves the model from the {post} segment. Customize binding in RouteServiceProvider or use custom keys like {post:slug}.

Implicit binding returns 404 automatically when the model is missing. Explicit binding in boot() helps when the URI key does not match the route parameter name.

Route::get('/posts/{post:slug}', [PostController::class, 'show']);

Step 4: Inspect routes with Artisan

Run php artisan route:list to see every registered route, middleware, and controller action. Filter with –name=posts or –path=api.

During debugging, route:list confirms whether a new route was registered and which middleware stack runs. Cached routes in production require route:clear after deploy when routes change.

php artisan route:list --name=posts

API Routes and Advanced Patterns

API routes live in routes/api.php and are prefixed with /api by default. They use the api middleware group with stateless JSON expectations and throttle limits.

Resource controllers and Route::apiResource reduce boilerplate for RESTful CRUD endpoints while excluding unnecessary create and edit routes.

Step 1: Register API resource routes

Route::apiResource('articles', ArticleController::class) registers index, store, show, update, and destroy without HTML form routes. Pair with API resources or Eloquent API resources for JSON shaping.

Version APIs with Route::prefix('v1')->group(…) so mobile clients can migrate gradually. Document endpoints with OpenAPI or Laravel Scribe for consumer teams.

Route::apiResource('articles', ArticleController::class);

Step 2: Route caching for production

php artisan route:cache compiles all routes into a single file for faster registration on high-traffic apps. Always clear the cache locally after editing routes.

Route caching fails if you use closure-based routes heavily; prefer controller actions in production apps that enable caching.

php artisan route:cache
php artisan route:clear

Step 3: Signed URLs and temporary links

URL::temporarySignedRoute generates links that expire, ideal for email verification or private downloads. The signed middleware validates the signature on incoming requests.

Never expose signed URLs in logs. Rotate APP_KEY carefully because invalidating old signatures affects outstanding email links.

URL::temporarySignedRoute('invoice.download', now()->addHours(2), ['invoice' => $id]);

Step 4: Subdomain and domain routing

Route::domain('{account}.myapp.test')->group(…) enables multi-tenant apps where the subdomain selects the tenant database or configuration.

Combine domain routing with middleware that resolves the tenant from the subdomain before controllers execute, keeping data isolated per customer.

Route::domain('{account}.myapp.test')->group(function () {
    Route::get('/', [TenantDashboardController::class, 'index']);
});

Aadyanex architects Laravel routing layers with clear API versioning, tenant isolation, and automated route tests so refactors never break client integrations.