Skip to content

Chapter 2.5: API Routes

Time to learn: 45 minutes


1. What is a Route? A Simple Explanation

Imagine your controller (PlanetController) is a large office building, and each of its methods (index, store, show) is a separate department doing its job.

A Route is the directory sign at the building's entrance. It says:

  • "If someone arrives at the /planets address with a GET method — send them to the index department (show all)."
  • "If someone arrives at the /planets address with a POST method carrying a package (data) — send them to the store department (create new)."

Without routes, no request from the outside world would find the right department in your code. The main file for these "address signs" in an API is routes/api.php.

In Laravel 11+, there is no "API address book" by default. We created it ourselves by running the php artisan install:api command. Now we have the routes/api.php file—this is the main control center for all our API's routes.

The key difference between api.php and web.php:

  • /api prefix: Laravel automatically adds /api to all URLs from this file. The /planets route becomes /api/planets.
  • "Stateless": There are no sessions or cookies here like in a regular web application. Each request is independent and must contain all the information needed for authentication (usually an API token in the headers).

2. The Beginner's Path: Creating a Route Manually

Let's create a single route by hand to understand the principle. Our goal is to make the /api/planets URL show a list of all planets.

Open routes/api.php and write:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PlanetController; // Specify where our controller is

//                    (1)           (2)                     (3)
Route::get(      '/planets',    [PlanetController::class, 'index']     );
//   ^               ^                       ^
// (HTTP Method)   (URL Address)          (Which controller and method to call)

Let's break down this line:

  1. Route::get(...) — We are saying: "This route only works for GET requests."
  2. '/planets' — This is the URL that Laravel will listen for. With the /api prefix, the full address will be http://space-api.test/api/planets.
  3. [PlanetController::class, 'index'] — This is the "destination." We are saying: "When the request comes in, find the PlanetController class and call the index() method within it."

Now everything is connected! Request -> Route -> Controller -> Method.

What if we need to get a single planet by its ID? For example, /api/planets/5.

// Route to get a specific planet
Route::get('/planets/{planet}', [PlanetController::class, 'show']);

Here, {planet} is a "wildcard" or variable. Laravel understands that anything can be in its place (an ID, a slug). It then passes this value to the show(Planet $planet) method. This "magic," where Laravel finds the planet by its ID automatically, is called Route Model Binding.


3. The Master's Path: apiResource—One Line to Rule Them All

Creating each route manually (for index, show, store, update, destroy) is tedious. The developers of Laravel understand this, so they created a powerful helper: apiResource.

Delete everything we wrote and replace it with a single line:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PlanetController;

Route::apiResource('planets', PlanetController::class);

What does this one line do under the hood? It automatically creates a whole set of routes for the standard CRUD operations that we have already implemented in the controller.

Method URL Dispatches to Method Purpose
GET /api/planets index() Get a list of all planets
POST /api/planets store() Create a new planet
GET /api/planets/{planet} show() Show one specific planet
PUT/PATCH /api/planets/{planet} update() Update an existing planet
DELETE /api/planets/{planet} destroy() Delete a planet

You can see for yourself. Run this command in your terminal:

php artisan route:list --path=api

You will see a table with all the created routes. apiResource is your best friend for saving time when creating standard APIs.


4. Special Missions and Route Order

What if we need a custom route that isn't in apiResource? For example, to get a random planet at /api/planets/random.

Let's add it. But there is a critically important trap here that 9 out of 10 beginners fall into.

Incorrect Order (DOES NOT WORK!):

Route::apiResource('planets', PlanetController::class);
Route::get('/planets/random', [PlanetController::class, 'random']); // <-- WILL NOT WORK
Why? Laravel reads routes from top to bottom. It will see Route::apiResource, which created the GET /planets/{planet} route. When you request /planets/random, Laravel will think that "random" is the ID of a planet, try to find a planet with the ID "random" in the database, and you will get an error.

Correct Order (WORKS!):

<?php
use App\Http\Controllers\PlanetController;
use Illuminate\Support\Facades\Route;

// 1. Declare SPECIFIC routes first
Route::get('/planets/random', [PlanetController::class, 'random']);

// 2. Then, declare GENERAL routes with variables
Route::apiResource('planets', PlanetController::class);

⚠️ Important!

To test the api/planets/random route, you need to add a new handler in PlanetController:

<?php
public function random(Request $request)
{
   $planet = Planet::inRandomOrder()->first();
   return response()->json($planet);
}

The Rule: Always declare more specific routes (like /random) before more general and wildcard routes (like /{planet}).


5. Grouping: Bringing Order

When you have many routes, you can and should group them.

A. API Versioning To avoid breaking old applications that use your API in the future, it is common practice to add a version to the URL, for example, /api/v1/....

<?php
Route::prefix('v1')->group(function () {
    // All routes within this group will get the /v1 prefix
    // Final URL: /api/v1/planets
    Route::get('/planets/random', [PlanetController::class, 'random']);
    Route::apiResource('planets', PlanetController::class);
});

B. Protecting Routes (Middleware) Imagine that anyone can view planets, but only authenticated users can create, update, and delete them.

<?php
// Public routes, accessible to everyone
Route::get('/planets', [PlanetController::class, 'index']);
Route::get('/planets/{planet}', [PlanetController::class, 'show']);

// Group of routes requiring a "pass" (authentication)
Route::middleware('auth:sanctum')->group(function () {
    Route::post('/planets', [PlanetController::class, 'store']);
    Route::put('/planets/{planet}', [PlanetController::class, 'update']);
    Route::delete('/planets/{planet}', [PlanetController::class, 'destroy']);
});

Here, middleware('auth:sanctum') is like a security guard who checks the "pass" of everyone trying to access the routes within the group.


6. Testing with Postman

Now that all the routes are laid out, it's time to test them.

  1. If you are using Herd: Your site is already running at an address like http://space-api.test.
  2. If not: Start the local server with the command php artisan serve. The address will be http://localhost:8000.

Open Postman and send the requests:

  • GET http://space-api.test/api/planets
  • GET http://space-api.test/api/planets/random
  • POST http://space-api.test/api/planets (don't forget to add a JSON request body in the Body -> raw -> JSON tab).

Example POST request:

{
    "name": "Kepler-186f",
    "description": "The first Earth-sized planet in the habitable zone",
    "size_km": 15000,
    "solar_system": "Kepler-186",
    "is_habitable": true
}


7. Common Routing Errors

  1. 404 Not Found
    • Incorrect URL (/api/planet instead of /api/planets)
    • Forgot to run php artisan serve
  2. 405 Method Not Allowed
    • Incorrect HTTP method (e.g., GET instead of POST)
  3. Missing Controller
    • Typo in the controller name (PlanetControler)
  4. Route Name Collision
    • Duplicate route names

Review Quiz

1. The file for API routes in Laravel is:

2. The automatic prefix for API routes is:

3. The method to create 5 CRUD routes is:

4. `Route::prefix('v1')` is used for:

5. To view all routes:


🚀 Chapter Summary:

You have built the "hyperspace routes" for the space API! Now:

  • 🗺️ All endpoints are accessible via /api/...
  • 🔗 Resource routes are connected to the controller
  • 🛡️ Custom routes for special operations have been added
  • ✅ The routes have been tested with Postman

The universe is open for requests! Next, we will add protection against "space debris"—data validation.

📌 Checkpoint:

  1. Run php artisan route:list
  2. Make sure you see 5+ routes for planets
  3. Test GET /api/planets in your browser/Postman

⚠️ If you get a 404 error:

  • Check for Route::apiResource in routes/api.php
  • Make sure the server is running (php artisan serve)
  • For Windows: allow port 8000 in the firewall