技術 5 分鐘閱讀

Laravel API 設計最佳實踐:從 RESTful 到版本控制

Laravel API 設計最佳實踐:從 RESTful 到版本控制

前言

最近在幫客戶開發需要提供 API 的專案越來越多。整理了一些目前覺得不錯的 API 設計原則,特別針對 Laravel 框架。

RESTful 路由命名

基本原則

// routes/api.php
Route::prefix('v1')->group(function () {
    Route::apiResource('products', ProductController::class);
    Route::apiResource('orders', OrderController::class);
    Route::apiResource('orders.items', OrderItemController::class);
});

產生的路由:

MethodURIAction
GET/api/v1/productsindex
POST/api/v1/productsstore
GET/api/v1/products/{id}show
PUT/api/v1/products/{id}update
DELETE/api/v1/products/{id}destroy

命名原則

  • 用名詞複數:/products 而非 /product
  • 巢狀資源不要超過兩層
  • 查詢用 query string:/products?category=food&sort=price

API Resource(回應格式)

// app/Http/Resources/ProductResource.php
class ProductResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'price' => $this->price,
            'description' => $this->description,
            'category' => new CategoryResource($this->whenLoaded('category')),
            'created_at' => $this->created_at->toISOString(),
        ];
    }
}

在 Controller 中使用:

public function index()
{
    $products = Product::with('category')->paginate(15);
    return ProductResource::collection($products);
}

public function show(Product $product)
{
    return new ProductResource($product->load('category'));
}

統一錯誤處理

// app/Exceptions/Handler.php
public function render($request, Throwable $e)
{
    if ($request->expectsJson()) {
        if ($e instanceof ModelNotFoundException) {
            return response()->json([
                'error' => '找不到指定的資源',
                'code' => 'RESOURCE_NOT_FOUND',
            ], 404);
        }

        if ($e instanceof ValidationException) {
            return response()->json([
                'error' => '驗證失敗',
                'code' => 'VALIDATION_ERROR',
                'details' => $e->errors(),
            ], 422);
        }
    }

    return parent::render($request, $e);
}

版本控制

目前覺得最實用的做法是 URL 前綴:

// routes/api.php
Route::prefix('v1')->group(function () {
    Route::apiResource('products', V1\ProductController::class);
});

Route::prefix('v2')->group(function () {
    Route::apiResource('products', V2\ProductController::class);
});

認證與 Rate Limiting

搭配 Middleware 做好 API 保護:

// routes/api.php
Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
    Route::apiResource('orders', OrderController::class);
});

API 文件

推薦使用 Scribe 自動產生 API 文件:

composer require knuckleswtf/scribe
php artisan vendor:publish --tag=scribe-config
php artisan scribe:generate

搭配前端

如果你的前端使用 React,可以參考 Inertia.js 省去 API 層。但如果是開發給第三方使用的 API,上述的設計原則就很重要了。

小結

好的 API 設計能讓前端開發者(包括未來的自己)更輕鬆。花時間在設計階段把結構想清楚,後續維護會省下大量時間。

如果你需要類似的系統,歡迎聯繫討論。

#Laravel #API #RESTful
Thousand Lab

Thousand Lab