Routes map URLs to code. Controllers organize that code into classes. Together they form the entry point of every Laravel request.
Laravel has two main route files with different purposes:
| File | Purpose | Middleware |
|---|---|---|
routes/web.php | Browser routes — returns HTML views | web (sessions, CSRF, cookies) |
routes/api.php | API routes — returns JSON, stateless | api (rate limiting, no sessions) |
routes/console.php | Artisan closure commands | — |
routes/channels.php | WebSocket broadcast channels | — |
/api. So Route::get('/posts', ...) in api.php is reachable at /api/posts.<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
// GET — retrieve data / show a page
Route::get('/posts', [PostController::class, 'index']);
// POST — create a resource
Route::post('/posts', [PostController::class, 'store']);
// PUT — replace entire resource
Route::put('/posts/{id}', [PostController::class, 'update']);
// PATCH — partial update
Route::patch('/posts/{id}', [PostController::class, 'update']);
// DELETE — remove a resource
Route::delete('/posts/{id}', [PostController::class, 'destroy']);
// Match any HTTP method
Route::any('/webhook', [WebhookController::class, 'handle']);
// Match specific methods
Route::match(['get', 'post'], '/contact', [ContactController::class, 'handle']);
// {id} is required — URL must include it
Route::get('/posts/{id}', function ($id) {
return "Post ID: $id";
});
// Multiple parameters
Route::get('/posts/{postId}/comments/{commentId}', function ($postId, $commentId) {
return "Post $postId, Comment $commentId";
});
// {name?} is optional — has a default value
Route::get('/user/{name?}', function ($name = 'Guest') {
return "Hello, $name!";
});
// /user -> "Hello, Guest!"
// /user/Alice -> "Hello, Alice!"
// Restrict to numeric IDs only
Route::get('/posts/{id}', [PostController::class, 'show'])
->where('id', '[0-9]+');
// Restrict to alpha string
Route::get('/user/{name}', [UserController::class, 'show'])
->where('name', '[A-Za-z]+');
// Global constraint in RouteServiceProvider
// Route::pattern('id', '[0-9]+');
Naming routes lets you generate URLs without hardcoding paths — very useful in forms and redirects.
// Define named route
Route::get('/posts/{id}', [PostController::class, 'show'])->name('posts.show');
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
// Generate URL from name
$url = route('posts.show', ['id' => 5]);
// http://your-app.com/posts/5
// Redirect to named route
return redirect()->route('dashboard');
return redirect()->route('posts.show', ['id' => $post->id]);
In Blade templates:
<a href="{{ route('posts.show', $post->id) }}">Read Post</a>
<a href="{{ route('dashboard') }}">Dashboard</a>
// All routes get /admin prefix
Route::prefix('admin')->group(function () {
Route::get('/users', [AdminController::class, 'users']); // /admin/users
Route::get('/settings', [AdminController::class, 'settings']); // /admin/settings
});
// All routes require authentication
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::resource('posts', PostController::class);
});
Route::prefix('admin')
->middleware(['auth', 'admin'])
->name('admin.')
->group(function () {
Route::get('/dashboard', [AdminController::class, 'index'])->name('dashboard');
// Route name = admin.dashboard
// URL = /admin/dashboard
});
# Create a basic controller php artisan make:controller PostController # Create a resource controller (with all 7 CRUD methods pre-defined) php artisan make:controller PostController --resource # Create API resource controller (no create/edit — no HTML forms) php artisan make:controller Api/PostController --api
Generated resource controller skeleton:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index() { /* list all */ }
public function create() { /* show create form */ }
public function store(Request $request) { /* save new */ }
public function show(Post $post) { /* show one */ }
public function edit(Post $post) { /* show edit form */ }
public function update(Request $request, Post $post) { /* update */ }
public function destroy(Post $post) { /* delete */ }
}
| Method | HTTP Verb | URI | Route Name | Purpose |
|---|---|---|---|---|
index | GET | /posts | posts.index | List all posts |
create | GET | /posts/create | posts.create | Show create form |
store | POST | /posts | posts.store | Save new post |
show | GET | /posts/{id} | posts.show | Display one post |
edit | GET | /posts/{id}/edit | posts.edit | Show edit form |
update | PUT/PATCH | /posts/{id} | posts.update | Update existing post |
destroy | DELETE | /posts/{id} | posts.destroy | Delete post |
// Register all 7 routes in one line
Route::resource('posts', PostController::class);
// Only specific methods
Route::resource('posts', PostController::class)->only(['index', 'show']);
// Exclude specific methods
Route::resource('posts', PostController::class)->except(['destroy']);
Laravel automatically resolves Eloquent models from route parameters — no manual Post::find($id) needed.
// Without binding (manual lookup)
Route::get('/posts/{id}', function ($id) {
$post = Post::findOrFail($id); // throws 404 if not found
return view('posts.show', compact('post'));
});
// With implicit model binding (automatic — type-hint the model)
Route::get('/posts/{post}', function (Post $post) {
return view('posts.show', compact('post'));
// Laravel finds Post where id = {post}
// Returns 404 automatically if not found
});
// In a controller (same type-hint approach)
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
{post} must match the variable name $post in the method signature for implicit binding to work.Inspect all registered routes with:
php artisan route:list # Filter by name php artisan route:list --name=posts # Filter by URI php artisan route:list --path=api # Show middleware php artisan route:list -v
Example output:
GET|HEAD / ...
GET|HEAD api/posts posts.index
POST api/posts posts.store
GET|HEAD api/posts/{post} posts.show
PUT|PATCH api/posts/{post} posts.update
DELETE api/posts/{post} posts.destroy
php artisan route:cache in production to cache routes for faster resolution. Always run php artisan route:clear after adding new routes.