Laravel Rest Api 3.0 — як з однієї ресурсної класу зібрати типізований REST, метрики і OpenAPI

У матеріалі розбирається підхід Gautier Deleglise у пакеті lomkit/laravel-rest-api версії 3.0. Автор пакета повністю переробив внутрішню реалізацію, але зберіг ключову ідею: одна ресурсна клас перетворює Eloquent модель на повноцінний REST API.

1. Typed fields — одна декларація керує валідацією, фільтрами і відповідями

У гілці 2.x ресурс мав список колонок. У 3.0 ресурс описує поля як типізовані об'єкти: Text, Integer, Number, Date, Enum, Json тощо. Це змінює спосіб побудови API: вся логіка «знає» тип кожного поля.

Приклад ресурсної конфігурації, яку описує автор:

public function fields(): array
{
    return [
        ID::make('id'),
        Text::make('title')->rules('max:80')->creationRules('required'),
        Integer::make('seats')->rules('min:0'),
        Enum::make('status', ShowStatus::class)->offered([ShowStatus::Planned, ShowStatus::OnSale]),
        Date::make('starts_on')->creationRules('required'),
    ];
}

Ключова ідея: один опис поля використовується для кількох задач одразу.

  • Фільтри поважають тип — для кожного типу доступні лише релевантні оператори. Якщо клієнт надсилає несумісний оператор, API повертає 422. Це прибирає велику частину ручних перевірок.
  • Валідація записів — записи перевіряються за правилами, прив'язаними до поля (rules(), creationRules()). Помилки «прив'язані» до конкретного ключа, наприклад mutate.0.attributes.seats, що полегшує обробку помилок на фронтенді.
  • Типізовані відповіді — сервер повертає значення у «правильних» типах: числа як числа, порожній JSON як {}, а не null чи порожній рядок. Це зменшує кількість умов у клієнтському коді.
  • Керування допустимими значеннями enum — Enum::offered() обмежує, які константи клієнт може записати. Наприклад, статус «withdrawn» може залишатися доступним у старих рядках (читання і фільтрація), але його не можна встановити заново у нових записях. Це корисно для «застарілих, але ще присутніх» станів.

Практичний наслідок: замість того щоб дублювати список полів і правила по контролерах, формується єдина точка правди в ресурсі. Для великих проєктів це особливо помітно на підтримці.

2. OpenAPI документація через Scramble замість вбудованого генератора

У 2.x пакет мав власний генератор документації. У версії 3.0 його видалили і інтегрувалися зі стороннім інструментом Scramble. Логіка з опису полів перекладається в OpenAPI схеми.

Після встановлення Scramble ендпоінт /docs/api описує кожен ресурс і кожен маршрут:

  • структуру тіла запиту, сформовану з typed fields ресурсу;
  • валідаційні правила для кожного поля;
  • приклади запитів;
  • опис помилок у відповідях.

У тестовому наборі самого пакета приклади запитів із документації фактично відправляються до API, а відповіді звіряються з описом OpenAPI. Якщо реалізація і документація розходяться, падає тест. Це підхід «документація як контракт», який знижує ризик «мертвих» чи застарілих описів.

3. Метрики без контролерів — Value, Trend, Partition, Progress і Table

У 3.0 з'явився окремий шар для побудови метрик над ресурсом. Розробник описує метрику на ресурсі, і вона автоматично отримує маршрут без написання контролера.

Приклад, наведений в оригіналі, — розподіл шоу за статусами через метрику Partition:

class ShowsByStatus extends Partition
{
    public function calculate(Builder $query): PartitionResult
    {
        return $this->count($query, 'status');
    }
}

Відповідь ендпоінта містить ключ кожної групи, її напис (label) і значення (count):

{"data": {"type": "partition", "parts": [{"key": "planned", "label": "planned", "value": 2}, {"key": "sold_out", "label": "sold_out", "value": 1}], "other": null}}

Автор пакета виділяє кілька типів метрик:

  • Value — агрегати на кшталт лічильника, суми, середнього, медіани, перцентилів;
  • Trend — значення по часових відрізках (наприклад, кількість записів по днях);
  • Partition — розбивка за категоріями, як у прикладі зі статусами;
  • Progress — значення поточного прогресу відносно цілі;
  • Table — табличні метрики.

Особливості, на які звертає увагу автор:

  • Діапазони дат і часові зони — метрики враховують діапазони часу та часовий пояс, що дозволяє будувати, наприклад, тижневі чи місячні тренди для певної таймзони.
  • Порівняння з попереднім періодом — можна отримати дельту відносно попереднього часового інтервалу (наприклад, зміна кількості бронювань «тиждень до тижня»).
  • Кеш результатів — результати метрик кешуються, щоб не рахувати важкі агрегації на кожен запит.
  • Поважання політик і периметра — метрика рахує тільки ті рядки, які користувач має право бачити. Додатково клієнт може передавати ті самі фільтри, що й для пошуку, щоб ще звузити вибірку.

Такий підхід зручний для дашбордів: замість окремих контролерів та агрегацій поверх Eloquent, вся логіка метрик оформлена як класи, пов'язані з конкретним ресурсом.

4. Підтвердження для дій і масових змін через 428 і підписаний токен

У версії 3.0 з'явився вбудований механізм підтверджень для потенційно небезпечних операцій. Його можна ввімкнути для:

  • action;
  • mutate;
  • destroy;
  • restore;
  • force destroy.

Автор наводить приклад підтвердження для масового видалення шоу і пов'язаних квитків:

public function destroyConfirmation(Collection $models): ?Confirmation
{
    return Confirmation::make("Delete {$models->count()} shows and their tickets?");
}

Поводження ендпоінта у такому випадку:

  • перше звернення повертає статус 428 (Precondition Required), текст повідомлення і підписаний токен;
  • клієнт має повторити той самий запит, додавши токен, — тоді операція виконується;
  • сесія не використовується, додатковий ендпоінт для підтвердження не потрібен.

Це підходить, наприклад, для інтерфейсів, де користувач має явно підтвердити масове видалення або незворотні дії, не будуючи власну складну схему з тимчасовими станами чи сесіями.

5. Політики, транзакції і пошук — як змінилася «важка» логіка

Крім typed fields, документації і метрик, автор пакета виділяє низку змін у поведінці безпосередньо API.

5.1. Політики і периметр доступу

  • Політики виконуються для кожного ресурсу, до якого доходить запит, і для кожного рядка, який записується, ще до першого запису.
  • perimeterQuery() дозволяє обмежити кожен ресурс у рамках «тенанта» або «команди» на всіх рівнях пошуку. Це форма глобального, але керованого multi-tenant фільтра.

5.2. Комплексні операції запису в одній транзакції

Пакет у 3.0 дозволяє в одному запиті:

  • створювати батьківські й дочірні записи;
  • оновлювати існуючі;
  • attach/sync для зв'язків;
  • видаляти дочірні рядки.

Усе це виконується в одній транзакції. Через блок response клієнт може отримати назад створені або змінені рядки, а ресурси, для яких увімкнено versioned(), будуть повертати 409, якщо рядок змінився після того, як клієнт його прочитав. Це варіант оптимістичної блокування на рівні REST.

5.3. Actions з різними моделями транзакцій

Actions у 3.0 можуть виконуватися з різною «гранулярністю» транзакцій:

  • одна транзакція на весь запуск;
  • одна транзакція на «chunk»;
  • окрема транзакція для кожного рядка.

Є підтримка чергування chunk'ів і batch'ів з репортингом прогресу, що спрощує оргнанізацію довгих операцій, на кшталт масового перерахунку чи міграції даних.

5.4. Повторне використання пошуку за межами HTTP

Rest::search() дозволяє «програти» збережений пошуковий запит поза HTTP контекстом. Таким чином, job або експорт можуть користуватися тими ж фільтрами, політиками і правилами, що і HTTP API, без повторного опису логіки.

5.5. Пошук по тексту і інтеграції із Scout

Для текстового пошуку пакет підтримує кілька варіантів:

  • LIKE на рівні SQL;
  • full-text індекси MySQL, MariaDB та PostgreSQL;
  • Meilisearch, Typesense, Algolia через Laravel Scout.

Це дає змогу налаштовувати однакове REST API поверх різних бэкендів пошуку, залежно від можливостей інфраструктури.

5.6. Локалізація повідомлень

Усі повідомлення, що потрапляють до клієнта, у пакеті постачаються англійською та французькою мовами і підлаштовуються під locale запиту.

6. Автогенерація ресурсів і контроль у CI

Для пришвидшення старту проєкту автор пакета додав дві корисні Artisan-команди:

  • php artisan rest:quick-start — читає структуру таблиць і генерує ресурсні класи, контролери, стартові політики і маршрути;
  • php artisan rest:check — підходить для CI, виявляє, наприклад, відсутню політику або perimeter для ресурсу.

Крім того, пакет включає skill для Laravel Boost, завдяки чому AI асистент (описаний в оригінальному матеріалі) може генерувати ресурси в тому форматі, якого очікує пакет.

7. Вимоги до середовища і оновлення до 3.0

На відміну від гілки 2.x, Laravel Rest Api 3.0 має підвищені вимоги:

  • PHP версії 8.3+;
  • Laravel версії 13.30+.

Якщо застосунок працює на Laravel 12, автор рекомендує залишатися на гілці 2.x.

Встановлення 3.0 виглядає так:

Приклад коду в оригіналі

Оскільки у версії 3.0 є breaking changes по всьому пакету, команда пакета підготувала детальний гайд з оновлення: UPGRADE.md. Там розглянуті зміни по розділах, тому при міграції з 2.x варто орієнтуватися саме на нього, а не на окремі приклади з огляду.

Повні деталі релізу можна знайти у release notes, а також у офіційній документації https://laravel-rest-api.lomkit.com та репозиторії GitHub https://github.com/Lomkit/laravel-rest-api.


Джерело: Eric L. Barnes — Laravel News

Пошук

Закрити