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認証、エラーハンドリング、テスト手法、実務でつまずきやすいトラブルシューティングまでを網羅して分かりやすく解説します。
- LaravelでAPIを開発するメリット
- LaravelでAPIを作成する5つの基本ステップ【5分クイックスタート】
- 環境セットアップとAPI有効化(Laravel 11 / 12対応)
- APIルーティングの基本とベストプラクティス
- Laravel APIの基本アーキテクチャと役割分担
- 完全なCRUD APIの実装手順【実践コード例】
- JSONレスポンス設計とHTTPステータスコード
- API認証:Laravel Sanctumの実装手順
- エラーハンドリングのカスタマイズ(Laravel 11 / 12)
- APIの自動テスト実装(Featureテスト)
- 応用テクニック:外部API連携とドキュメント生成
- よくある質問とトラブルシューティング(FAQ)
- まとめ
- 関連記事
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ステップです。
- プロジェクト作成:
composer create-project laravel/laravel api-app - API機能の有効化:
php artisan install:api(Laravel 11 / 12では必須) - モデル・マイグレーション・コントローラー生成:
php artisan make:model Post -mcr --api - APIルーティング定義:
routes/api.phpにRoute::apiResource('posts', PostController::class);を記述 - ローカル起動と動作確認:
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フォーム表示メソッド(createやedit)が不要です。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.phpにLaravel\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.phpのallowed_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:apiでroutes/api.phpとSanctumを有効化 - ルーティング:
Route::apiResourceでRESTfulなCRUDエンドポイントを簡潔に定義 - 責務分離:Controller(制御)、FormRequest(検証)、Model(データ)、Resource(JSON整形)の4層構造を徹底
- API認証:Laravel Sanctumによるトークン認証またはSPA Cookie認証を活用
- 自動テスト:
$this->getJson()や$this->postJson()で品質を担保
本記事の手順に沿って、拡張性が高く保守しやすいモダンなLaravel APIを構築してみてください。

コメント