EthicsComputerCourses
Join training +91 90532 10052

Routing

Laravel · Lesson 5 of 30 Beginner 8 min read Updated

By the end of this lesson you will be able to create web pages at any URL, read values from the URL, give routes names, group admin routes together and list every route in your app. Routing is the first thing Laravel does with each request, so it is a good place to start writing code.

What a route is

A route is a rule that says: "when a request comes for this URL with this HTTP method, run this code". All web routes live in routes/web.php.

Think of the Mumbai dabbawalas. Each tiffin has a code painted on the lid. The code tells every dabbawala on the way which train, which station and which office it goes to. A route is that code for a request: it tells Laravel exactly where to deliver it.

The simplest route returns a string:

PHP
<?php

use Illuminate\Support\Facades\Route;

Route::get('/about', function () {
    return 'Vidya Coaching, Rohtak. Classes for JEE, NEET and Boards.';
});

Route::get() takes two things: the URL path and the code to run. The code here is a closure (an unnamed function). Test it with curl while php artisan serve is running:

Terminal
curl http://127.0.0.1:8000/about
Output
Vidya Coaching, Rohtak. Classes for JEE, NEET and Boards.

HTTP methods

Each route also matches an HTTP method, the kind of request:

MethodUsed forLaravel
GETShowing a page or dataRoute::get()
POSTSubmitting a form, creating somethingRoute::post()
PUT / PATCHUpdating somethingRoute::put(), Route::patch()
DELETEDeleting somethingRoute::delete()

There is also Route::match(['get', 'post'], ...) for several methods and Route::any(...) for all of them. Browsers can only send GET and POST from HTML forms; you will see in Blade Templates how Laravel fakes PUT and DELETE.

Route parameters

Most URLs carry data, like a course code. Wrap a part of the path in curly braces to make it a parameter. Laravel passes it into your function.

PHP
Route::get('/courses/{code}', function (string $code) {
    return "You asked for course {$code}";
});

Route::get('/batches/{year}/{month?}', function (int $year, ?int $month = null) {
    return $month ? "Batches starting in {$month}/{$year}" : "All batches of {$year}";
})->whereNumber(['year', 'month']);

The ? in {month?} makes the parameter optional, so the function needs a default value. whereNumber() is a constraint: the route only matches when those parts are digits. Here is what four requests return:

Output
GET /courses/NEET-C   -> You asked for course NEET-C
GET /batches/2027     -> All batches of 2027
GET /batches/2027/4   -> Batches starting in 4/2027
GET /batches/abc      -> 404 Not Found

Other constraints include whereAlpha(), whereAlphaNumeric(), whereUuid(), whereIn('mode', ['cash', 'upi']) and where('code', '[A-Z0-9-]+') with your own regular expression.

Returning different responses

Whatever your route returns becomes the response. Laravel converts it for you:

  • A string becomes an HTML response.
  • An array or Eloquent model becomes JSON with the right header.
  • view('name') renders a Blade template.
  • redirect('/somewhere') sends the browser to another URL.
PHP
Route::get('/fees.json', function () {
    return ['JEE-F' => 85000, 'NEET-C' => 42000];
});

Route::redirect('/admission', '/courses/JEE-F');

Route::view('/contact', 'contact');
Output
GET /fees.json   -> {"JEE-F":85000,"NEET-C":42000}
GET /admission   -> HTTP/1.1 302 Found, Location: /courses/JEE-F

Route::view() is a shortcut for routes that only show a template with no logic.

Named routes

Hard-coding URLs like /courses/JEE-F all over your templates is risky. If you later rename the URL, every link breaks. Instead, give the route a name and generate URLs from it.

PHP
Route::get('/courses/{code}', function (string $code) {
    return "You asked for course {$code}";
})->name('courses.show');

Now the route() helper builds the URL for you:

PHP
echo route('courses.show', ['code' => 'JEE-F']);
Output
http://localhost:8000/courses/JEE-F

The host comes from APP_URL in .env when there is no current request (for example in Tinker). In a browser request, it uses the real host. Change the path in one place, and every link updates.

Route groups

Admin pages often share a URL prefix, a name prefix and the same login check. A group applies these once:

PHP
Route::prefix('admin')->name('admin.')->group(function () {
    Route::get('/dashboard', fn () => 'Admin dashboard')->name('dashboard');
    Route::get('/reports', fn () => 'Fee reports')->name('reports');
});

The URLs become /admin/dashboard and /admin/reports, and their names become admin.dashboard and admin.reports. In Middleware & Sessions you will add ->middleware('auth') to the same group so only logged-in staff can open it.

Route model binding

Very often a parameter is a database ID or code, and the first thing you do is load the row. Route model binding does it for you. Type-hint a model, and Laravel finds the record. If it does not exist, Laravel returns a 404 page automatically.

PHP
use App\Models\Course;

Route::get('/course-info/{course:code}', function (Course $course) {
    return "{$course->name} costs Rs {$course->fee}";
});

The :code part says "look up by the code column". Without it, Laravel uses the id column.

Output
GET /course-info/C10-M  -> Class 10 Boards Maths costs Rs 18000
GET /course-info/XYZ    -> 404 Not Found

You will create the Course model and its table in Migrations, Seeders & Factories.

Resource routes and route:list

For a typical "manage students" section you need seven routes: list, create form, store, show, edit form, update and delete. One line creates all of them and points them at a controller:

PHP
use App\Http\Controllers\StudentController;

Route::resource('students', StudentController::class);

To see every route in the app, run route:list. The --except-vendor option hides routes added by packages:

Terminal
php artisan route:list --except-vendor
Output
GET|HEAD   /  ....................................... routes/web.php:7
GET|HEAD   about ................................... routes/web.php:11
GET|HEAD   admin/dashboard ........ admin.dashboard › routes/web.php:36
GET|HEAD   admin/reports .............. admin.reports › routes/web.php:37
ANY        admission ....... Illuminate\Routing › RedirectController
GET|HEAD   batches/{year}/{month?} ................. routes/web.php:19
GET|HEAD   contact .................................................
GET|HEAD   course-info/{course:code} ............... routes/web.php:27
GET|HEAD   courses/{code} ............. courses.show › routes/web.php:15
GET|HEAD   fees.json ............................... routes/web.php:23
GET|HEAD   students .......... students.index › StudentController@index
POST       students .......... students.store › StudentController@store
GET|HEAD   students/create . students.create › StudentController@create
GET|HEAD   students/{student} .. students.show › StudentController@show
PUT|PATCH  students/{student} students.update › StudentController@update
DELETE     students/{student} students.destroy › StudentController@destroy
GET|HEAD   students/{student}/edit students.edit › StudentController@edit
GET|HEAD   {fallbackPlaceholder} ................... routes/web.php:42

                                                     Showing [18] routes

The last line comes from a fallback route, which runs when nothing else matches. It is a friendly custom 404:

PHP
Route::fallback(function () {
    return response('Page not found. Try /courses', 404);
});

Common mistakes

  • Route order with similar paths. /students/{student} defined before /students/create will catch "create" as a student ID. Define fixed paths first, or use Route::resource(), which orders them for you.
  • Forgetting the default for optional parameters. {month?} needs ?int $month = null in the function, or PHP throws an error.
  • Hard-coding URLs in views. Use route('name') so renaming a URL does not break links.
  • Expecting a new route to appear on a live server. If the server ran php artisan route:cache, run it again after changing routes.

Key points

  • Routes in routes/web.php map an HTTP method and URL to a closure or controller.
  • {name} creates a parameter, {name?} an optional one; whereNumber() and friends add constraints.
  • Strings become HTML, arrays become JSON; Route::redirect() and Route::view() are shortcuts.
  • Name routes with ->name() and build URLs with route().
  • Group shared prefixes, names and middleware; use model binding to load records and return 404 automatically.
  • php artisan route:list shows every route, its name and its action.

Exercise

Add these routes to the coaching app:

  1. GET /results/{year} that only accepts a four-digit year and returns "JEE results for 2026" (with the given year).
  2. A group with prefix accounts and name prefix accounts., containing GET /dues named dues that returns "Pending fees list".
  3. Print the URL of the dues route with route().
Show solution
PHP
Route::get('/results/{year}', function (int $year) {
    return "JEE results for {$year}";
})->where('year', '[0-9]{4}');

Route::prefix('accounts')->name('accounts.')->group(function () {
    Route::get('/dues', fn () => 'Pending fees list')->name('dues');
});
PHP
echo route('accounts.dues');
Output
http://localhost:8000/accounts/dues

The regular expression [0-9]{4} allows exactly four digits, so /results/26 returns 404. The group adds both the URL prefix and the name prefix.

Quick Quiz

1. What does {month?} mean in a route path?

2. What happens with Route::get('/course-info/{course:code}', fn (Course $course) => ...) when no course has that code?

3. Why should you use named routes?

4. How many routes does Route::resource('students', ...) create?

Learn Laravel with a mentor

Free lessons get you started. Our PHP & MySQL Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call