Пошук уроків, статей та іншого контенту
Порівнюємо два підходи до проєктування API та розбираємо реальні сценарії використання.
REST і GraphQL — це підходи до проєктування API, через які клієнтські застосунки взаємодіють із сервером.
Вони вирішують схожі задачі, але по-різному визначають:
як описуються ресурси та операції;
хто контролює структуру відповіді;
як кешуються дані;
як версіонуються зміни;
як організовуються авторизація, моніторинг і документація.
Важливо: REST — це архітектурний стиль, а GraphQL — мова запитів і специфікація виконання таких запитів. На практиці обидва підходи потребують додаткових рішень щодо автентифікації, бази даних, кешування та обробки помилок.
У REST API сервер представляє дані у вигляді ресурсів. Кожен ресурс має URL, а HTTP-методи визначають тип операції:
GET — отримати ресурс або колекцію;
POST — створити ресурс;
PUT — повністю замінити ресурс;
PATCH — частково оновити ресурс;
DELETE — видалити ресурс.
Наприклад:
GET /api/users/42Відповідь може мати такий вигляд:
{
"id": 42,
"name": "Олена",
"email": "olena@example.com"
}Для створення користувача клієнт надсилає:
POST /api/users
Content-Type: application/json
{
"name": "Олена",
"email": "olena@example.com"
}REST зазвичай використовує стандартні можливості HTTP:
статус-коди;
заголовки;
кешування;
умовні запити;
авторизацію;
Content Negotiation для різних форматів представлення.
У великому застосунку API може містити такі ресурси:
GET /api/products
GET /api/products/15
POST /api/products
PATCH /api/products/15
DELETE /api/products/15
GET /api/products/15/reviews
POST /api/products/15/reviewsКлієнт зазвичай отримує наперед визначену структуру відповіді. Якщо сторінці потрібні назва товару, ціна, автор і кількість відгуків, сервер або повертає всі ці поля, або для цього створюють окремий endpoint.
GraphQL зазвичай використовує одну endpoint-адресу, наприклад:
POST /graphqlКлієнт надсилає запит, у якому явно описує поля, що йому потрібні:
query ProductDetails($id: ID!) {
product(id: $id) {
id
name
price
author {
name
}
reviews {
rating
}
}
}Сервер повертає структуру, яка відповідає запиту:
{
"data": {
"product": {
"id": "15",
"name": "Клавіатура",
"price": 2499,
"author": {
"name": "Компанія Example"
},
"reviews": [
{
"rating": 5
}
]
}
}
}Схема GraphQL описує доступні типи, поля, аргументи та операції. Завдяки цьому клієнт і сервер мають формальний контракт.
GraphQL підтримує три основні типи операцій:
query — читання даних;
mutation — зміна даних;
subscription — отримання оновлень у реальному часі через довготривале з’єднання.
Приклад мутації:
mutation CreateProduct($input: ProductInput!) {
createProduct(input: $input) {
id
name
price
}
}Змінні передаються окремо:
{
"input": {
"name": "Клавіатура",
"price": 2499
}
}REST найчастіше має багато endpoint-ів, кожен із яких відповідає певному ресурсу або операції.
GraphQL зазвичай має одну endpoint-адресу, а тип операції та необхідні дані описуються в тілі запиту.
Це не означає, що REST завжди має багато endpoint-ів, а GraphQL — лише один. Але така модель є типовою для кожного підходу.
У REST структуру відповіді визначає сервер. Клієнт може отримати поля, які йому не потрібні, або змушений зробити кілька запитів.
GraphQL дає клієнту можливість обрати конкретні поля. Це зменшує ризик:
отримати зайві дані;
створити окремий endpoint для кожного типу екрана;
виконати багато послідовних запитів для побудови одного представлення.
Водночас свобода клієнта створює додаткові ризики для сервера: складні запити можуть вимагати значних ресурсів.
REST добре узгоджується з HTTP-кешуванням. Для GET-запитів можна використовувати:
Cache-Control;
ETag;
Last-Modified;
кеші CDN і проксі.
У GraphQL багато запитів надсилаються на одну адресу, часто через POST. Через це стандартне HTTP-кешування стає складнішим. Доводиться застосовувати:
клієнтське кешування;
кешування на рівні GraphQL-клієнта;
persisted queries;
спеціальну логіку на CDN або сервері.
GraphQL також можна використовувати з GET для запитів читання, але це не усуває всіх складнощів зі стабільністю та ідентифікацією запитів.
У REST поширений підхід із версіями в URL:
/api/v1/products
/api/v2/productsТакож версію можна передавати через заголовки або Content Negotiation.
У GraphQL зазвичай намагаються підтримувати одну схему та поступово вилучати застарілі поля. Для цього поле позначають як deprecated, а клієнти переводять на нову версію.
Це не робить GraphQL автоматично «безверсійним». Несумісні зміни схеми все одно потребують плану міграції.
REST API можна описувати за допомогою OpenAPI. Якість документації залежить від того, наскільки повно її підтримує команда.
GraphQL-схема є частиною самого API-контракту. Клієнтські інструменти можуть використовувати її для:
автодоповнення;
перевірки запитів;
генерації типів;
дослідження доступних операцій.
Однак схема не описує всю поведінку системи. Наприклад, окремо потрібно документувати правила авторизації, обмеження частоти запитів і бізнес-правила.
У REST HTTP-статус зазвичай одразу показує результат операції:
200 OK — успішне читання;
201 Created — створення ресурсу;
400 Bad Request — некоректний запит;
401 Unauthorized — відсутня автентифікація;
403 Forbidden — недостатньо прав;
404 Not Found — ресурс не знайдено;
409 Conflict — конфлікт станів;
500 Internal Server Error — помилка сервера.
У GraphQL відповідь може містити одночасно data і errors:
{
"data": {
"product": null
},
"errors": [
{
"message": "Товар не знайдено",
"path": ["product"]
}
]
}Тому GraphQL-клієнт має перевіряти не лише HTTP-статус, а й поле errors.
REST часто є хорошим вибором, коли потрібні простота та передбачуваність.
Основні переваги:
зрозуміла модель ресурсів;
природне використання HTTP;
просте кешування GET-запитів;
зручна інтеграція з CDN, проксі та сторонніми клієнтами;
просте тестування через curl, браузер або стандартні HTTP-інструменти;
зрозумілі статус-коди;
низький поріг входу для нових розробників;
зручна публікація стабільного API для партнерів.
REST особливо добре підходить для CRUD-систем, у яких клієнт працює з очевидними ресурсами: користувачами, замовленнями, товарами, документами.
GraphQL корисний там, де клієнтам потрібні різні представлення одних і тих самих даних.
Основні переваги:
клієнт запитує лише потрібні поля;
один запит може отримати пов’язані дані;
зручно обслуговувати вебклієнт і мобільні застосунки з різними потребами;
схема дає формальний типізований контракт;
можна поступово додавати поля без створення нової версії endpoint-а;
зручно будувати агрегований API над кількома внутрішніми сервісами;
сильні інструменти для перевірки запитів і генерації типів.
GraphQL не обов’язково зменшує кількість роботи на сервері. Він переносить частину контролю над формою відповіді від сервера до клієнта.
Клієнт може надіслати глибокий або надто великий запит. Наприклад, користувачі можуть містити замовлення, замовлення — товари, а товари — відгуки та рекомендації.
Для захисту сервера застосовують:
обмеження глибини запиту;
обмеження складності;
ліміти кількості вузлів;
обмеження розміру запиту;
тайм-аути;
persisted queries;
контроль доступу до окремих полів.
Резолвер для списку об’єктів може виконати окремий запит до бази для кожного пов’язаного об’єкта.
Наприклад:
отримати 100 замовлень;
для кожного замовлення окремо отримати користувача;
виконати 100 додаткових запитів.
Це називають проблемою N+1. Для її розв’язання використовують пакетування та кешування запитів на час виконання операції, часто за допомогою підходу DataLoader.
У REST endpoint і HTTP-метод часто достатні, щоб зрозуміти, що відбувається. У GraphQL один endpoint може обслуговувати тисячі різних запитів.
Тому важливо логувати:
назву операції;
вибрані поля;
час виконання;
кількість помилок;
використані резолвери;
складність запиту;
ідентифікатор клієнта.
Не варто безконтрольно записувати в логи весь запит: він може містити конфіденційні дані.
У REST доступ часто перевіряють на рівні endpoint-а. У GraphQL один запит може містити поля з різними правилами доступу.
Наприклад, користувач може мати право бачити ім’я, але не мати права бачити номер телефону. Авторизацію потрібно перевіряти не лише на рівні всієї операції, а й на рівні ресурсів та полів, де це необхідно.
Обирайте REST, якщо:
API переважно працює з ресурсами та CRUD-операціями;
потрібне просте HTTP-кешування;
API буде публічним або партнерським;
клієнтів небагато, і їхні потреби добре відомі;
команда хоче мінімізувати інфраструктурну складність;
важливі прозорі статус-коди та стандартні HTTP-інструменти;
потрібні великі файли, потокова передача або звичайні HTTP-завантаження;
API має інтегруватися з системами, які очікують REST-подібний контракт.
Для бекенду адміністративної панелі може бути достатньо такого API:
GET /api/orders?status=pending&page=2
GET /api/orders/501
PATCH /api/orders/501
POST /api/orders/501/cancelЯкщо структура екранів стабільна, GraphQL не обов’язково принесе суттєву користь.
Обирайте GraphQL, якщо:
є кілька клієнтів із різними потребами;
мобільний застосунок має обмежений трафік;
один екран збирає дані з багатьох пов’язаних ресурсів;
потрібен типізований контракт між командами;
клієнти часто розвиваються незалежно від сервера;
API агрегує кілька мікросервісів;
структура відповідей часто змінюється залежно від конкретного екрана.
Сторінка товару може містити:
основну інформацію;
ціну та залишок;
продавця;
варіанти доставки;
рейтинги;
рекомендації;
персональні пропозиції.
Мобільному клієнту можуть бути потрібні лише назва, ціна та одне зображення, а вебклієнту — повний набір даних. GraphQL дає змогу описати ці два представлення без створення окремого endpoint-а для кожного екрана.
Не обов’язково вибирати лише один підхід для всієї системи.
Поширені варіанти:
REST для публічних ресурсів і GraphQL для вебклієнтів;
REST для завантаження файлів, вебхуків і простих операцій;
GraphQL як BFF-шар над кількома REST-сервісами;
REST для внутрішніх мікросервісів і GraphQL на межі системи;
GraphQL для читання складних представлень, REST для команд і масових операцій.
Наприклад, GraphQL може звертатися до внутрішніх REST API, баз даних або інших сервісів. У такій архітектурі GraphQL є не заміною всієї інфраструктури, а шаром, адаптованим до потреб клієнтів.
Перед вибором відповідайте на такі запитання:
Чи мислить домен чіткими ресурсами?
Скільки буде клієнтів і наскільки вони відрізняються?
Чи потрібно часто комбінувати пов’язані дані?
Наскільки важливе HTTP-кешування?
Чи має команда досвід підтримки GraphQL?
Як буде обмежено складність запитів?
Як реалізуються авторизація на рівні полів і ресурсів?
Як API документуватиметься та спостерігатиметься?
Чи потрібні потокові відповіді, великі файли або вебхуки?
Чи є вимоги сторонніх партнерів до формату API?
Якщо відповіді переважно вказують на прості ресурси, стандартний HTTP і стабільні клієнти — почніть із REST.
Якщо головна проблема полягає в різноманітності клієнтських запитів та агрегації даних — розгляньте GraphQL.
GraphQL може зменшити кількість мережевих запитів і обсяг зайвих даних, але складний запит може бути важким для бази даних і резолверів.
Продуктивність потрібно вимірювати, а не припускати. Важливі індекси, пакетування, кешування, ліміти та профілювання.
REST залишається практичним вибором для багатьох API. Простий і добре спроєктований REST часто кращий за складний GraphQL, який команда не може належно захищати та підтримувати.
Без обмежень клієнт може сформувати надто дорогий запит. Глибину, складність і час виконання потрібно контролювати на рівні сервера.
GraphQL-запит, який виглядає як один мережевий виклик, може спричинити сотні запитів до бази даних. Перевіряйте виконання резолверів і використовуйте пакетування.
Не кожна зміна потребує нової версії. Зазвичай безпечно додавати нові поля, але небезпечно видаляти або змінювати значення наявних полів без міграції клієнтів.
Резолвери мають координувати отримання даних, а не містити всю бізнес-логіку. Правила домену краще зберігати в окремих сервісах або use case-ах, щоб їх можна було повторно використовувати.
Кешування не варто додавати в кінці. Для REST слід заздалегідь визначити політики HTTP-кешу, а для GraphQL — стратегію ідентифікації, оновлення та інвалідації даних.
У GraphQL клієнт може запитати будь-яке доступне поле. У REST сервер також не повинен повертати зайві персональні дані. Контроль доступу та мінімізація відповіді важливі незалежно від підходу.
REST і GraphQL не є універсальними замінами один одного.
REST варто обирати, коли потрібні:
простий ресурсний API;
стандартні HTTP-механізми;
передбачуване кешування;
легка інтеграція та обслуговування.
GraphQL варто обирати, коли потрібні:
різні представлення даних для клієнтів;
агрегація пов’язаних ресурсів;
типізована схема;
гнучкі запити;
незалежний розвиток клієнтів і сервера.
Найкращий вибір визначається не популярністю технології, а вимогами системи. У багатьох проєктах ефективним рішенням стає поєднання обох підходів: REST там, де достатньо стандартного HTTP, і GraphQL там, де клієнтам потрібна гнучка композиція даних.