Урок 12

Практические паттерны API

Единый формат ответа

Один и тот же формат во всех эндпоинтах упрощает клиенту жизнь:

{
  "data": {...},
  "meta": {...}
}

Для ошибок:

{
  "message": "...",
  "errors": {...}
}

Именование эндпоинтов

  • Ресурсы во множественном числе: /posts, /users
  • Вложенность: /posts/{post}/comments
  • Действия над ресурсом выражаются методом, а не URL: DELETE /posts/5, а не POST /posts/5/delete

Исключения

Иногда действие не вписывается в CRUD. Тогда используют глагол:

POST /posts/{post}/publish
POST /posts/{post}/archive
POST /users/{user}/ban

Версионирование с самого начала

Даже если сейчас одна версия — заложи /v1. Добавить версию потом сложнее.

Частичное обновление

PATCH — для частичного обновления. PUT — для полного:

public function update(PatchPostRequest $request, Post $post)
{
    $post->update($request->validated());
    return new PostResource($post);
}

Идемпотентность

POST не идемпотентен: два одинаковых запроса создадут две записи. Для платежей и заказов используют idempotency key:

$key = $request->header('Idempotency-Key');

if ($key && Cache::has("idem:{$key}")) {
    return Cache::get("idem:{$key}");
}

// обработка
$response = new OrderResource($order);

if ($key) {
    Cache::put("idem:{$key}", $response, now()->addDay());
}

return $response;

Rate limiting

Ограничение частоты запросов защищает от злоупотреблений:

Route::middleware('throttle:60,1')->group(function () {
    Route::apiResource('posts', PostController::class);
});

Для аутентифицированных клиентов лимит выше:

RateLimiter::for('api', function ($request) {
    return $request->user()
        ? Limit::perMinute(1000)->by($request->user()->id)
        : Limit::perMinute(60)->by($request->ip());
});

CORS

Если фронт на другом домене — нужен CORS. Laravel настраивается в config/cors.php:

'allowed_origins' => ['https://app.example.com'],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'supports_credentials' => true,

Кеширование

Ответы API можно кешировать:

return Cache::remember('posts.index', 3600, function () {
    return PostResource::collection(Post::all());
});

Итоги

  • API — это контракт между сервером и клиентами
  • Ресурсы проектируются вокруг сущностей, а не URL
  • Версионирование закладывается сразу
  • Ошибки возвращаются в едином формате
  • Аутентификация — через токены
  • Документация обязательна
  • Rate limiting защищает от злоупотреблений