Introduction

Laravel 11 and 13 no longer ship with routes/api.php by default. When you need a stateless API, you run php artisan install:api — a single command that creates the routes file, installs Laravel Sanctum for token authentication, and wires up the api middleware group.

Key Concepts

  • php artisan install:api: Scaffolds everything needed to expose a REST API.
  • Laravel Sanctum: A lightweight API token package that Laravel installs automatically.
  • auth:sanctum middleware: Protects API routes by validating the bearer token on each request.
  • bootstrap/app.php ->withRouting(): Where the API prefix (/api by default) is configured.

Real World Context

Most full-stack Laravel apps do not need an API on day one. The install-on-demand approach keeps the starter lean and avoids unused files in projects that only serve Blade pages. The moment you add a mobile app or an SPA, you run install:api and get a production-ready foundation.

Deep Dive

Running the Installer

bash
php artisan install:api

The command:

  1. Requires laravel/sanctum via Composer.
  2. Publishes Sanctum's config and migrations.
  3. Runs the new migrations (creating a personal_access_tokens table).
  4. Creates routes/api.php with a sample me endpoint.
  5. Updates bootstrap/app.php to register the API routes via ->withRouting(api: __DIR__.'/../routes/api.php', ...).

The Generated routes/api.php

php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

The auth:sanctum middleware accepts a bearer token in the Authorization header and resolves the matching user. Every route defined in routes/api.php is prefixed with /api automatically.

Issuing a Token

php
// In a controller that handles POST /login
$user = User::where('email', $request->email)->first();

if (! $user || ! Hash::check($request->password, $user->password)) {
    throw ValidationException::withMessages([
        'email' => ['The provided credentials are incorrect.'],
    ]);
}

return response()->json([
    'token' => $user->createToken('mobile-app')->plainTextToken,
]);

The client stores the token and sends it on subsequent requests as Authorization: Bearer <token>.

Customising the Prefix

Edit bootstrap/app.php:

php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        apiPrefix: 'api/v1',  // /api/v1/users instead of /api/users
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->create();

Common Pitfalls

  1. Looking for routes/api.php in a fresh project — It does not exist until you run install:api. Do not assume old Laravel 10 tutorials still apply.
  2. Forgetting to migrate Sanctum — The installer runs migrations automatically, but if you copy a project without its database, you must re-run php artisan migrate to create the token table.

Best Practices

  1. Scope tokens per device — Pass a human-readable name to createToken('mobile-app') so users can revoke one device without logging out everywhere.
  2. Use abilities — Sanctum supports per-token abilities (createToken('mobile', ['post:create'])). Prefer granular abilities over wide-open tokens.

Summary

  • php artisan install:api is the Laravel 11+/13 way to scaffold an API.
  • It installs Sanctum and wires up auth:sanctum for bearer-token authentication.
  • routes/api.php is prefixed with /api, customisable via bootstrap/app.php.
  • Issue tokens with $user->createToken(name)->plainTextToken.

Code Examples

php
// First, in your shell:
// php artisan install:api

// Then in routes/api.php:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::apiResource('posts', PostController::class);
});
✓ Completed