A route connects a URL (plus an HTTP method) to the code that should answer it.
๐๏ธThink of routes as a hotel receptionist. A guest asks for โroom 204โ (the URL), and the receptionist knows exactly who to send them to (your code). If nobody is assigned to that room, the guest gets a polite โnot foundโ (404).
Every page starts with a route
It's the front door
No route, no page. Every request into your app is matched against your route list first.
One map of your app
Open routes/web.php and you can see every URL your app answers, all in one place.
Change URLs safely
Named routes let you rename a URL once, and every link updates automatically.
What happens when someone visits /about
Laravel keeps routes in the routes/ folder. You'll spend most of your time in two files:
| File | Used for | Comes with |
|---|---|---|
routes/web.php | Pages people visit in a browser | Sessions, cookies, CSRF protection |
routes/api.php | JSON endpoints for apps & JS frontends | Automatic /api prefix, stateless. Created by php artisan install:api |
routes/console.php | Custom Artisan commands & schedules | No HTTP at all |
Build your routing skills
Each step adds one idea. Try each snippet in a fresh Laravel project as you go.
-
Your first route
A route needs three things: the HTTP method (
get), the URL (/hello), and the code to run. Here, the code is a closure (an unnamed function).<?php use Illuminate\Support\Facades\Route; Route::get('/hello', function () { return 'Hello, Laravel! ๐'; });Run
php artisan serveand visithttp://127.0.0.1:8000/hello. Whatever the closure returns is sent back to the browser. -
Return a view instead of text
Real pages are HTML, so you'll usually return a Blade view.
view('about')loadsresources/views/about.blade.php.Route::get('/about', function () { return view('about', ['team' => 'Blueprint Crew']); }); // Shortcut when there's no logic at all: Route::view('/contact', 'contact');The array passes data to the view. Inside Blade you can now write
{{ $team }}. -
Capture parts of the URL with parameters
Wrap a URL segment in
{curly braces}and Laravel hands its value to your function. Add?to make a parameter optional, and use a constraint to reject bad input.Route::get('/posts/{id}', function (string $id) { return "Showing post #{$id}"; })->whereNumber('id'); // /posts/abc โ 404 // Optional parameter with a default value Route::get('/greet/{name?}', function (string $name = 'friend') { return "Hi, {$name}!"; }); Route::get('/users/{user}/posts/{post}', function (string $user, string $post) { return "User {$user}, post {$post}"; });โน๏ธ Coming later: route model bindingOnce you have models (Module 05), type-hint
Post $postinstead ofstring $id. Laravel then fetches the post from the database for you, or returns a 404 automatically. -
Give routes a name
A named route is a nickname for a URL. You build links from the name, never by typing the path by hand.
Route::get('/posts/{id}', function (string $id) { // ... })->name('posts.show');<a href="{{ route('posts.show', ['id' => 42]) }}">Read post 42</a> {{-- renders: /posts/42 --}}return redirect()->route('posts.show', ['id' => 42]); -
Group routes that share settings
When many routes share a prefix, middleware or controller, group them so you don't repeat yourself.
use App\Http\Controllers\Admin\DashboardController; Route::prefix('admin') // URLs start with /admin ->name('admin.') // names start with admin. ->middleware('auth') // must be logged in ->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']) ->name('dashboard'); // โ admin.dashboard, /admin/dashboard Route::get('/reports', [DashboardController::class, 'reports']) ->name('reports'); // โ admin.reports, /admin/reports });Notice
[DashboardController::class, 'index']. That's how a route points to a controller method instead of a closure. You'll build controllers in the next lesson. -
Match the right HTTP verb
The same URL can do different things depending on the method. Browsers use
GETto read andPOSTto submit forms. The others are used for updating and deleting.Route::get('/posts', ...); // list posts Route::post('/posts', ...); // save a new post Route::put('/posts/{id}', ...); // replace a post Route::patch('/posts/{id}', ...); // update part of a post Route::delete('/posts/{id}', ...); // delete a post Route::match(['get', 'post'], '/search', ...); Route::any('/webhook', ...);
Avoid this, do that
<a href="/posts/{{ $id }}">Read</a>
Hard-coded paths break silently when you change a URL, and you'll be hunting through dozens of templates.
<a href="{{ route('posts.show', $id) }}">Read</a>
Change the URL once in web.php, and every link that uses the route name keeps working.
Route::get('/posts/{id}', ...);
Route::get('/posts/create', ...); // never reached!
/posts/create matches {id} first, with id = "create".
Route::get('/posts/create', ...);
Route::get('/posts/{id}', ...)->whereNumber('id');
Put specific routes first, and constrain parameters so they only match what they should.
This prints a table of every route, its method, name and handler. It's the fastest way to debug a 404.
php artisan route:list
php artisan route:list --path=admin # filter
In a fresh Laravel project, build these routes:
GET /: returns the text โHomeโ.GET /products/{id}: only accepts numbers, is namedproducts.show, and returns โProduct #IDโ.- A group with the prefix
/shopcontaining/cartand/checkout, namedshop.cartandshop.checkout. - Run
php artisan route:listand check that all four appear.
Key takeaways
- Routes live in
routes/web.php(pages) androutes/api.php(JSON). - A route = HTTP method + URL + code to run (closure or controller method).
{param}captures URL segments;{param?}makes them optional.- Always name routes and generate links with
route('name'). - Groups share prefixes, name prefixes and middleware.
php artisan route:listis your routing debugger.