株式会社WR

株式会社WR

WEB TOTAL CONSULTING

LaravelでRESTful APIを構築する——JSON応答・認証・レート制限
ブログ一覧へ
技術ブログ

LaravelでRESTful APIを構築する——JSON応答・認証・レート制限

LaravelでフロントエンドやモバイルアプリのバックエンドAPIを構築する方法を解説します。JSONリソース・Sanctum認証・レート制限の実装例を紹介します。

LaravelでRESTful APIを構築する前に

RESTful APIはリソース指向の設計思想に基づくWebAPIの設計スタイルです。LaravelはAPIルーティング・認証・レート制限・JSON応答など、RESTful API構築に必要な機能を標準で備えています。

本記事では実務で使えるAPI設計のベストプラクティスとLaravelでの実装方法を解説します。


ルーティングの設計

// routes/api.php
use App\Http\Controllers\Api\V1\PostController;
use App\Http\Controllers\Api\V1\AuthController;

Route::prefix('v1')->name('api.v1.')->group(function () {

    // 認証不要エンドポイント
    Route::post('/auth/login',    [AuthController::class, 'login'])->name('auth.login');
    Route::post('/auth/register', [AuthController::class, 'register'])->name('auth.register');

    // 公開リソース(読み取りのみ)
    Route::apiResource('posts', PostController::class)->only(['index', 'show']);

    // 認証必須
    Route::middleware('auth:sanctum')->group(function () {
        Route::post('/auth/logout', [AuthController::class, 'logout']);
        Route::apiResource('posts', PostController::class)
             ->except(['index', 'show']);
        Route::apiResource('comments', CommentController::class);
    });
});

JSON応答の統一——API Resourceクラス

php artisan make:resource PostResource でAPI Resourceを作成します。

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id'         => $this->id,
            'type'       => 'post',
            'attributes' => [
                'title'        => $this->title,
                'excerpt'      => $this->excerpt,
                'body'         => $this->when(
                    $request->routeIs('*.show'), // 詳細取得のみbodyを含む
                    $this->body
                ),
                'category'     => $this->category,
                'published_at' => $this->published_at?->toIso8601String(),
                'created_at'   => $this->created_at->toIso8601String(),
            ],
            'relationships' => [
                'author' => new UserResource($this->whenLoaded('author')),
                'tags'   => TagResource::collection($this->whenLoaded('tags')),
            ],
        ];
    }
}
// コントローラー
public function index(Request $request): JsonResponse
{
    $posts = Post::with(['author', 'tags'])
                 ->published()
                 ->paginate($request->integer('per_page', 15));

    return PostResource::collection($posts)->response();
}

public function show(Post $post): PostResource
{
    $post->load(['author', 'tags', 'comments']);
    return new PostResource($post);
}

統一されたエラーレスポンス

// app/Exceptions/Handler.php
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;

public function register(): void
{
    $this->renderable(function (ModelNotFoundException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'error' => [
                    'code'    => 'NOT_FOUND',
                    'message' => 'リソースが見つかりません',
                ],
            ], 404);
        }
    });

    $this->renderable(function (ValidationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'error' => [
                    'code'    => 'VALIDATION_ERROR',
                    'message' => 'バリデーションエラー',
                    'details' => $e->errors(),
                ],
            ], 422);
        }
    });
}

認証——Laravel Sanctum

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
class AuthController extends Controller
{
    public function login(LoginRequest $request): JsonResponse
    {
        $credentials = $request->only('email', 'password');

        if (!Auth::attempt($credentials)) {
            return response()->json([
                'error' => ['code' => 'UNAUTHORIZED', 'message' => 'メールアドレスまたはパスワードが正しくありません'],
            ], 401);
        }

        $user = Auth::user();

        // 既存トークンを削除して新しいトークンを発行
        $user->tokens()->delete();
        $token = $user->createToken('api-token', ['*'], now()->addDays(30));

        return response()->json([
            'data' => [
                'token'      => $token->plainTextToken,
                'expires_at' => $token->accessToken->expires_at?->toIso8601String(),
                'user'       => new UserResource($user),
            ],
        ]);
    }

    public function logout(Request $request): JsonResponse
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['message' => 'ログアウトしました']);
    }
}

レート制限

// app/Providers/RouteServiceProvider.php(または bootstrap/app.php)
RateLimiter::for('api', function (Request $request) {
    return [
        // 認証済みユーザーは1分間に60リクエスト
        Limit::perMinute(60)->by($request->user()?->id),
        // 未認証は1分間に20リクエスト(IPアドレスベース)
        Limit::perMinute(20)->by($request->ip()),
    ];
});

// ログインエンドポイントは厳しく制限
RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});

ページネーションのカスタマイズ

// カーソルベースページネーション(大量データに向いている)
public function index(): JsonResponse
{
    $posts = Post::published()
                 ->orderBy('id')
                 ->cursorPaginate(20);

    return response()->json([
        'data' => PostResource::collection($posts->items()),
        'meta' => [
            'next_cursor' => $posts->nextCursor()?->encode(),
            'prev_cursor' => $posts->previousCursor()?->encode(),
            'per_page'    => $posts->perPage(),
            'has_more'    => $posts->hasMorePages(),
        ],
    ]);
}

APIドキュメント——Scribe

composer require --dev knuckleswtf/scribe
php artisan scribe:generate

コントローラーにPHPDocを書くだけで自動的にAPIドキュメントを生成できます。


まとめ

LaravelのAPI Resource・Sanctum認証・レート制限を組み合わせることで、本番運用に耐えるRESTful APIをすばやく構築できます。弊社では予約管理システム・ECバックエンドなどのAPI開発実績があります。

LaravelによるAPI開発のご相談はお気軽にどうぞ。

Category 技術ブログ

Related Posts

関連記事

開発・技術のご相談はお気軽に

お見積りは無料です。まずはお気軽にご相談ください。

お問い合わせ →