Laravel API作成完全ガイド|初心者から実践までの手順・ルーティング・Sanctum認証

Laravel入門

Laravel APIとは、PHPの代表的なWebアプリケーションフレームワーク「Laravel」を用いて、JSON形式などのデータをやり取りするRESTful API(Web API)を構築する仕組み全般を指します。Laravelは標準でAPIルーティング、リクエストバリデーション、Eloquent ORM、JSON変換(API Resource)、認証(Laravel Sanctum)、レートリミット(スロットリング)などの強力な機能を備えており、React・Vue・Next.jsなどのモダンフロントエンドやモバイルアプリのバックエンドAPIを極めて効率的に開発できます。

本記事では、Laravel 11 / Laravel 12の最新仕様(bootstrap/app.php構造およびinstall:apiコマンド)を前提に、LaravelでのAPI作成手順、ルーティング設計、CRUDコントローラー、FormRequestバリデーション、API ResourceによるJSON整形、Sanctum認証、エラーハンドリング、テスト手法、実務でつまずきやすいトラブルシューティングまでを網羅して分かりやすく解説します。

  1. LaravelでAPIを開発するメリット
  2. LaravelでAPIを作成する5つの基本ステップ【5分クイックスタート】
  3. 環境セットアップとAPI有効化(Laravel 11 / 12対応)
    1. 新規プロジェクトの作成
    2. APIルーティングの有効化(install:apiコマンド)
  4. APIルーティングの基本とベストプラクティス
    1. 1. リソースルート(Route::apiResource)
    2. 2. 個別ルートの定義とグループ化
    3. 3. CORS(Cross-Origin Resource Sharing)の設定
  5. Laravel APIの基本アーキテクチャと役割分担
  6. 完全なCRUD APIの実装手順【実践コード例】
    1. ステップ1:モデルとマイグレーションの作成
    2. ステップ2:FormRequestによるバリデーションの作成
    3. ステップ3:API Resourceによるレスポンスの定義
    4. ステップ4:APIコントローラーの実装
  7. JSONレスポンス設計とHTTPステータスコード
    1. バリデーションエラー時のJSON構造(422)
  8. API認証:Laravel Sanctumの実装手順
    1. 1. Userモデルの設定
    2. 2. 認証コントローラーの実装(ログイン・トークン発行・ログアウト)
    3. 3. routes/api.phpでの認証保護
  9. エラーハンドリングのカスタマイズ(Laravel 11 / 12)
  10. APIの自動テスト実装(Featureテスト)
  11. 応用テクニック:外部API連携とドキュメント生成
    1. 外部APIを呼び出す(Httpファサード)
    2. OpenAPI / Swaggerドキュメントの自動生成
  12. よくある質問とトラブルシューティング(FAQ)
    1. Q1. routes/api.phpが見つかりません。どこにありますか?
    2. Q2. 未認証時に401エラーのJSONではなく、ログイン画面(/login)へリダイレクトされてしまいます。
    3. Q3. フロントエンドからのAPIリクエストでCORSエラーが出ます。
    4. Q4. API Resourceでリレーションを含めるとSQLのクエリ数が爆発します(N+1問題)。
  13. まとめ
  14. 関連記事

LaravelでAPIを開発するメリット

LaravelがAPIバックエンドとして世界中で広く選ばれている理由は以下の通りです。

  • 洗練されたルーティングとAPI専用リソース定義Route::apiResourceでRESTfulなエンドポイントを1行で定義可能
  • 安全で柔軟な入力検証:FormRequestにより、コントローラーから検証ロジックを完全に分離し、エラー時は自動で422 Unprocessable ContentのJSONを返却
  • API Resourceによる堅牢なJSONレスポンス層:DBスキーマを隠蔽し、リレーションの条件付き読み込みや型変換を一元管理
  • 公式認証パッケージ(Sanctum)の標準サポート:SPA向けCookieセッション認証とモバイル/外部向けのBearerトークン認証の両方に簡単対応
  • 直感的なAPI自動テスト機能$this->getJson()$this->postJson()を使って、HTTPステータスやJSON構造を簡単に検証可能

LaravelでAPIを作成する5つの基本ステップ【5分クイックスタート】

Laravelで最小構成のRESTful APIを構築する流れは次の5ステップです。

  1. プロジェクト作成composer create-project laravel/laravel api-app
  2. API機能の有効化php artisan install:api(Laravel 11 / 12では必須)
  3. モデル・マイグレーション・コントローラー生成php artisan make:model Post -mcr --api
  4. APIルーティング定義routes/api.phpRoute::apiResource('posts', PostController::class);を記述
  5. ローカル起動と動作確認php artisan serveでサーバーを立ち上げ、curlやPostmanでリクエストを送信

環境セットアップとAPI有効化(Laravel 11 / 12対応)

API開発を始める前に、PHP 8.2以上とComposerがインストールされている環境を用意します。

新規プロジェクトの作成

composer create-project laravel/laravel api-app
cd api-app

APIルーティングの有効化(install:apiコマンド)

Laravel 11以降(Laravel 12含む)では、初期状態のアプリケーションを軽量化するため、routes/api.phpファイルやAPI用ミドルウェア設定が初期状態では含まれていません。以下のArtisanコマンドを実行してAPI機能を有効化します。

php artisan install:api

このコマンドを実行すると、以下のセットアップが自動的に行われます:

  • routes/api.phpファイルの自動生成
  • bootstrap/app.phpへのAPIルート(/apiプレフィックス)の自動登録
  • API認証用パッケージLaravel Sanctumのインストール、設定ファイル追加、およびpersonal_access_tokensテーブルのマイグレーション生成

install:apiコマンドの詳しい仕組みやオプションについてはinstall:api — APIをインストールするコマンドで詳しく解説しています。

APIルーティングの基本とベストプラクティス

API用のエンドポイントはすべてroutes/api.phpに定義します。このファイル内に記述したルートには、自動的に/apiのURIプレフィックスとステートレスなapiミドルウェアグループ(レートリミットやJSON変換など)が適用されます。

1. リソースルート(Route::apiResource)

RESTful APIでは、Webページ用のHTMLフォーム表示メソッド(createedit)が不要です。Route::apiResourceを使うと、APIに必要な5つのアクション(index, store, show, update, destroy)だけをまとめて定義できます。

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\PostController;

Route::apiResource('posts', PostController::class);

生成されるエンドポイント一覧は以下の通りです:

HTTPメソッド URI コントローラーアクション 用途・説明
GET /api/posts index 投稿一覧の取得
POST /api/posts store 新規投稿の作成
GET /api/posts/{post} show 指定した投稿1件の取得
PUT / PATCH /api/posts/{post} update 指定した投稿の更新
DELETE /api/posts/{post} destroy 指定した投稿の削除

2. 個別ルートの定義とグループ化

特定のアクションのみを個別に定義したり、バージョン(v1, v2)ごとにプレフィックスを付ける場合は、以下のようにグループ化します。

use App\Http\Controllers\Api\V1\PostController;
use App\Http\Controllers\Api\V1\CommentController;

Route::prefix('v1')->group(function () {
    Route::get('/posts', [PostController::class, 'index']);
    Route::post('/posts', [PostController::class, 'store']);
    Route::get('/posts/{post}', [PostController::class, 'show']);
    Route::put('/posts/{post}', [PostController::class, 'update']);
    Route::delete('/posts/{post}', [PostController::class, 'destroy']);

    // ネストしたサブリソース(浅いネスト化: shallow)
    Route::apiResource('posts.comments', CommentController::class)->shallow();
});

ルーティングのより高度な活用法(パラメータ制約、フォールバックルート、ミドルウェアの適用順序など)はLaravel API Route設定方法:RESTful API構築の基本とベストプラクティスもあわせて参照してください。

3. CORS(Cross-Origin Resource Sharing)の設定

React(Next.js)やVue(Nuxt)など別ドメインや別ポートのフロントエンドからAPIを呼び出す際、ブラウザの同一生成元ポリシーによりCORSエラーが発生する場合があります。Laravelではconfig/cors.php(存在しない場合はphp artisan config:publish corsで生成)で許可オリジンを設定できます。

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => [
        env('FRONTEND_URL', 'http://localhost:3000'),
    ],
    'allowed_headers' => ['*'],
    'supports_credentials' => true,
];

Laravel APIの基本アーキテクチャと役割分担

Laravelで保守性の高いAPIを構築する際は、以下の4つのレイヤーに責務を明確に分離します。

レイヤー・クラス 主な役割 生成コマンド
Controller(コントローラー) HTTPリクエストの受付、ビジネスロジックの呼び出し、レスポンスの返却 php artisan make:controller Api/PostController --api
FormRequest(フォームリクエスト) 入力値のバリデーション、型チェック、ユーザー認可チェック php artisan make:request StorePostRequest
Eloquent Model(モデル) データベースとの接続・CRUD操作・リレーション定義 php artisan make:model Post -m
API Resource(リソース) EloquentモデルからJSONレスポンスへの整形・変換・フィールド隠蔽 php artisan make:resource PostResource

完全なCRUD APIの実装手順【実践コード例】

「記事(Post)リソース」を題材に、モダンな設計に沿ったCRUD APIをゼロから実装してみましょう。

ステップ1:モデルとマイグレーションの作成

マイグレーションとモデルを生成します。

php artisan make:model Post -m

生成されたdatabase/migrations/xxxx_create_posts_table.phpを編集します:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('title');
            $table->text('content');
            $table->string('status')->default('draft'); // draft, published
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

マイグレーションを実行してテーブルを作成します:

php artisan migrate

app/Models/Post.phpでマスアサインメントとリレーションを設定します:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'title',
        'content',
        'status',
    ];

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

ステップ2:FormRequestによるバリデーションの作成

コントローラー内に検証ルールをベタ書きせず、FormRequestクラスに分離します。make:ruleによるカスタムルールの追加も可能です。

php artisan make:request StorePostRequest
php artisan make:request UpdatePostRequest

app/Http/Requests/StorePostRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true; // 認可ポリシーを使う場合はここで制御
    }

    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'status'  => ['sometimes', 'in:draft,published'],
        ];
    }

    public function messages(): array
    {
        return [
            'title.required'   => 'タイトルは必須項目です。',
            'content.required' => '本文を入力してください。',
            'status.in'        => 'ステータスは draft または published を指定してください。',
        ];
    }
}

app/Http/Requests/UpdatePostRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title'   => ['sometimes', 'required', 'string', 'max:255'],
            'content' => ['sometimes', 'required', 'string'],
            'status'  => ['sometimes', 'in:draft,published'],
        ];
    }
}

ステップ3:API Resourceによるレスポンスの定義

データベースの生データをそのまま返却すると、不要な内部カラムが漏洩したり、フロントエンドが期待するフォーマットとの調整が難しくなります。JsonResourceを使用してレスポンス層を独立させます。

php artisan make:resource PostResource

app/Http/Resources/PostResource.php

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource
{
    /**
     * リソースを配列に変換する
     */
    public function toArray(Request $request): array
    {
        return [
            'id'         => $this->id,
            'title'      => $this->title,
            'content'    => $this->content,
            'status'     => $this->status,
            'author'     => $this->whenLoaded('user', function () {
                return [
                    'id'   => $this->user->id,
                    'name' => $this->user->name,
                ];
            }),
            'created_at' => $this->created_at?->toISOString(),
            'updated_at' => $this->updated_at?->toISOString(),
        ];
    }
}

Tips(N+1問題の防止)$this->whenLoaded('user')を使用すると、コントローラー側でeager loading(with)された時のみリレーションデータを含め、無駄なSQLクエリの発行(N+1問題)を防ぐことができます。

ステップ4:APIコントローラーの実装

make:controllerコマンドでAPIコントローラーを作成します。

php artisan make:controller Api/PostController --api

app/Http/Controllers/Api/PostController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Http\Resources\PostResource;
use App\Models\Post;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;

class PostController extends Controller
{
    /**
     * 記事一覧取得(ページネーション対応)
     * GET /api/posts
     */
    public function index(): AnonymousResourceCollection
    {
        $posts = Post::with('user')
            ->latest()
            ->paginate(15);

        return PostResource::collection($posts);
    }

    /**
     * 新規記事作成
     * POST /api/posts
     */
    public function store(StorePostRequest $request): JsonResponse
    {
        // 認証ユーザーと紐付けて作成
        $post = $request->user()->posts()->create($request->validated());

        return (new PostResource($post))
            ->response()
            ->setStatusCode(201); // 201 Created
    }

    /**
     * 記事1件取得(ルートモデルバインディング)
     * GET /api/posts/{post}
     */
    public function show(Post $post): PostResource
    {
        $post->load('user');
        return new PostResource($post);
    }

    /**
     * 記事更新
     * PUT/PATCH /api/posts/{post}
     */
    public function update(UpdatePostRequest $request, Post $post): PostResource
    {
        $post->update($request->validated());

        return new PostResource($post);
    }

    /**
     * 記事削除
     * DELETE /api/posts/{post}
     */
    public function destroy(Post $post): Response
    {
        $post->delete();

        return response()->noContent(); // 204 No Content
    }
}

JSONレスポンス設計とHTTPステータスコード

RESTful APIでは、適切なHTTPステータスコードを返すことがクライアント側での正しいエラーハンドリングに不可欠です。

ステータスコード 名称 Laravelでの使用場面 レスポンス実装例
200 OK 成功 GETによるデータ取得、PUT/PATCHによる更新成功 return new PostResource($post);
201 Created リソース作成完了 POSTによる新規リソース作成成功時 return (new PostResource($post))->response()->setStatusCode(201);
204 No Content コンテンツなし DELETEによる削除成功時(ボディなし) return response()->noContent();
400 Bad Request 不正なリクエスト リクエストパラメータの論理不正など return response()->json(['message' => 'Bad request'], 400);
401 Unauthorized 未認証 APIトークンが未付与または無効な場合 Sanctumミドルウェアが自動返却
403 Forbidden 権限なし 認証済みだが他人のデータを操作しようとした場合 Policy認可失敗時に自動返却
404 Not Found 未検出 指定したIDのレコードやURLが存在しない場合 ルートモデルバインディングにより自動返却
422 Unprocessable Content バリデーション違反 FormRequestの検証ルールを満たさない場合 FormRequestがエラー詳細JSONを自動返却
429 Too Many Requests リクエスト数超過 レートリミットの上限に達した場合 Throttleミドルウェアが自動返却
500 Server Error サーバー内部エラー 未処理の例外が発生した場合 Laravel例外ハンドラーが返却

バリデーションエラー時のJSON構造(422)

FormRequestでバリデーションエラーが発生した場合、Laravelは自動的に以下の統一されたJSONレスポンスを返します:

{
  "message": "タイトルは必須項目です。 (and 1 more error)",
  "errors": {
    "title": [
      "タイトルは必須項目です。"
    ],
    "content": [
      "本文を入力してください。"
    ]
  }
}

API認証:Laravel Sanctumの実装手順

APIの認証には、Laravel公式のLaravel Sanctumを使用するのが現代の標準です。php artisan install:apiを実行した時点でSanctumはインストールされています。

1. Userモデルの設定

app/Models/User.phpLaravel\Sanctum\HasApiTokensトレイトが組み込まれていることを確認します。

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    // ...
}

2. 認証コントローラーの実装(ログイン・トークン発行・ログアウト)

app/Http/Controllers/Api/AuthController.phpを作成し、トークン発行と失効を実装します。

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class AuthController extends Controller
{
    /**
     * ログイン(アクセストークン発行)
     */
    public function login(Request $request): JsonResponse
    {
        $request->validate([
            'email'       => ['required', 'email'],
            'password'    => ['required'],
            'device_name' => ['sometimes', 'string'],
        ]);

        $user = User::where('email', $request->email)->first();

        if (! $user || ! Hash::check($request->password, $user->password)) {
            throw ValidationException::withMessages([
                'email' => ['メールアドレスまたはパスワードが正しくありません。'],
            ]);
        }

        $deviceName = $request->input('device_name', 'default-token');
        // トークン生成(必要に応じてアビリティ・権限を指定可能)
        $token = $user->createToken($deviceName, ['posts:create', 'posts:read'])->plainTextToken;

        return response()->json([
            'token' => $token,
            'user'  => [
                'id'    => $user->id,
                'name'  => $user->name,
                'email' => $user->email,
            ],
        ]);
    }

    /**
     * ログアウト(現在のトークンを削除)
     */
    public function logout(Request $request): JsonResponse
    {
        $request->user()->currentAccessToken()->delete();

        return response()->json(['message' => 'ログアウトしました。']);
    }
}

3. routes/api.phpでの認証保護

認証が必要なエンドポイントはauth:sanctumミドルウェアで保護します。

use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\PostController;

// 公開ルート
Route::post('/login', [AuthController::class, 'login']);

// 認証必須ルート
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::post('/logout', [AuthController::class, 'logout']);

    Route::apiResource('posts', PostController::class);
});

クライアントからは、HTTPリクエストヘッダーにAuthorization: Bearer <トークン文字列>を付与してリクエストを送ります:

curl -X GET http://localhost:8000/api/posts \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|xxxxxxxxxxxxxxxxxxxxxxxx"

Sanctumのより詳細なトークン管理やSPA Cookie認証についてはLaravel Sanctumの使い方完全ガイド、OAuth2サーバー構築が必要な場合はLaravel Passportの使い方完全ガイドを参照してください。

エラーハンドリングのカスタマイズ(Laravel 11 / 12)

Laravel 11 / 12では、従来のapp/Exceptions/Handler.phpが廃止され、すべての例外ハンドリングはbootstrap/app.php->withExceptions()クロージャに集約されました。

APIリクエスト時に一貫したJSONフォーマットでエラーを返したい場合は、以下のように設定します:

// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        // APIリクエストでNotFoundが発生した場合のJSONレスポンスをカスタマイズ
        $exceptions->render(function (NotFoundHttpException $e, Request $request) {
            if ($request->is('api/*') || $request->expectsJson()) {
                return response()->json([
                    'status'  => false,
                    'message' => '指定されたリソースが見つかりませんでした。',
                ], 404);
            }
        });
    })->create();

APIの自動テスト実装(Featureテスト)

LaravelはAPI専用のテストメソッドを豊富に備えています。make:testコマンドでテストファイルを生成し、エンドポイントの振る舞いを検証します。

php artisan make:test Api/PostApiTest

tests/Feature/Api/PostApiTest.php

<?php

namespace Tests\Feature\Api;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;

class PostApiTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_list_posts(): void
    {
        $user = User::factory()->create();
        Post::factory()->count(3)->create(['user_id' => $user->id]);

        $response = $this->getJson('/api/posts');

        $response->assertStatus(200)
            ->assertJsonCount(3, 'data')
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'title', 'content', 'status', 'created_at']
                ],
                'links',
                'meta',
            ]);
    }

    public function test_authenticated_user_can_create_post(): void
    {
        $user = User::factory()->create();
        Sanctum::actingAs($user); // Sanctum認証状態をシミュレート

        $payload = [
            'title'   => 'テスト記事のタイトル',
            'content' => 'テスト記事の本文内容です。',
            'status'  => 'published',
        ];

        $response = $this->postJson('/api/posts', $payload);

        $response->assertStatus(201)
            ->assertJsonPath('title', 'テスト記事のタイトル');

        $this->assertDatabaseHas('posts', [
            'title'   => 'テスト記事のタイトル',
            'user_id' => $user->id,
        ]);
    }

    public function test_validation_error_when_title_is_missing(): void
    {
        $user = User::factory()->create();
        Sanctum::actingAs($user);

        $response = $this->postJson('/api/posts', [
            'content' => '本文のみのデータ',
        ]);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['title']);
    }
}

テストを実行します:

php artisan test --filter=PostApiTest

応用テクニック:外部API連携とドキュメント生成

LaravelはAPIを提供する側だけでなく、外部APIを呼び出すクライアント機能やドキュメント自動化機能も充実しています。

外部APIを呼び出す(Httpファサード)

決済サービスや外部WebサービスのREST APIをLaravelから呼び出す場合は、GuzzleベースのHttpファサードを使用します。

use Illuminate\Support\Facades\Http;

$response = Http::withToken('your-api-token')
    ->timeout(5)
    ->retry(3, 100)
    ->get('https://api.example.com/v1/users');

if ($response->successful()) {
    $data = $response->json();
}

HTTPクライアントの実践的な活用法はLaravelとGuzzleを使ったAPIリクエストの効率的な実装ガイド、レスポンスのJSON解析はPHP json_decodeの使い方解説を参考にしてください。

OpenAPI / Swaggerドキュメントの自動生成

作成したAPI仕様書をチームメンバーやフロントエンドエンジニアと共有する際は、ScrambleやL5-Swagger等のパッケージを活用してOpenAPIドキュメントを自動生成できます。詳細はLaravel OpenAPIを活用して効率的にAPIドキュメントを生成する方法で解説しています。

よくある質問とトラブルシューティング(FAQ)

Q1. routes/api.phpが見つかりません。どこにありますか?

A. Laravel 11 / 12では初期状態でroutes/api.phpが存在しません。ターミナルでphp artisan install:apiを実行するとファイルが作成され、APIルーティングが有効化されます。

Q2. 未認証時に401エラーのJSONではなく、ログイン画面(/login)へリダイレクトされてしまいます。

A. クライアントからのリクエストヘッダーにAccept: application/jsonが付与されていないことが主な原因です。このヘッダーがない場合、Laravelは通常のブラウザリクエストと判断してWeb用ログイン画面へのリダイレクト(302)を試みます。APIリクエスト時は必ずAccept: application/jsonを含めてください。

Q3. フロントエンドからのAPIリクエストでCORSエラーが出ます。

A. config/cors.phpallowed_originsに対象フロントエンドのURL(例:http://localhost:3000)が指定されているか確認してください。また、認証Cookieを使う場合はsupports_credentials => trueにする必要があります。

Q4. API Resourceでリレーションを含めるとSQLのクエリ数が爆発します(N+1問題)。

A. コントローラーでデータを取得する際にPost::with('user')->get()のようにEager Loadingを行い、API Resource側では$this->whenLoaded('user')を使って条件付き展開を行ってください。

まとめ

LaravelでのAPI開発は、フレームワークが提供する規約とコンポーネントを適切に組み合わせることで、高速かつ堅牢に実現できます。

  • 最新環境php artisan install:apiroutes/api.phpとSanctumを有効化
  • ルーティングRoute::apiResourceでRESTfulなCRUDエンドポイントを簡潔に定義
  • 責務分離:Controller(制御)、FormRequest(検証)、Model(データ)、Resource(JSON整形)の4層構造を徹底
  • API認証:Laravel Sanctumによるトークン認証またはSPA Cookie認証を活用
  • 自動テスト$this->getJson()$this->postJson()で品質を担保

本記事の手順に沿って、拡張性が高く保守しやすいモダンなLaravel APIを構築してみてください。

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント