Пошук уроків, статей та іншого контенту
Порівнюємо два підходи до проєктування API та розбираємо реальні сценарії використання.
Обидва підходи вирішують ту саму базову задачу — дати клієнту (браузеру, мобільному застосунку, іншому серверу) спосіб отримати й змінити дані через мережу. Різниця — у тому, хто вирішує, які саме дані повернути в конкретній відповіді: у REST це заздалегідь визначає сервер (кожен ендпоінт має фіксовану форму відповіді), у GraphQL — клієнт, формулюючи запит із потрібними полями.
REST API організовує дані навколо ресурсів і стандартних HTTP-методів: GET — отримати, POST — створити, PUT/PATCH — оновити, DELETE — видалити. Кожен ресурс має свій URL, а форма відповіді на конкретний ендпоінт фіксована — сервер завжди повертає той самий набір полів, незалежно від того, чи клієнту потрібні всі вони.
GET /api/users/1
GET /api/users/1/postsЯкщо інтерфейсу потрібне ім'я користувача разом із заголовками його останніх постів, знадобиться два окремих запити до двох різних ендпоінтів — REST не дає стандартного способу «злити» відповіді кількох ресурсів в один запит.
Ці два запити ілюструють класичну пару проблем REST API: якщо ендпоінт GET /api/users/1 повертає повний профіль користувача (десятки полів), а екрану потрібне лише ім'я — це over-fetching (зайві дані передаються по мережі). А необхідність робити окремий запит за постами — це under-fetching (одного запиту не вистачає, потрібно кілька).
GraphQL дозволяє клієнту описати точну форму потрібних даних в одному запиті — сервер повертає рівно ті поля, які запросили, навіть якщо вони розкидані по кількох пов'язаних сутностях:
query {
user(id: "1") {
name
posts {
title
}
}
}На сервері за кожне поле відповідає окрема функція — резолвер. GraphQL-схема описує типи даних і зв'язки між ними (наприклад, User має поле posts типу [Post]), а виконання запиту — це виклик відповідних резолверів для кожного запитаного поля.
GraphQL особливо виправданий, коли клієнтів багато (web, мобільний застосунок, партнерські інтеграції) і кожному потрібен свій набір полів — REST змусив би або створювати окремі ендпоінти під кожен випадок, або віддавати всім однаковий «важкий» об'єкт із зайвими даними.
REST природно вписується в HTTP-кешування: GET-запит на конкретний URL можна кешувати на рівні браузера, CDN чи проксі-сервера просто за допомогою стандартних заголовків (Cache-Control, ETag) — інфраструктура HTTP вже вміє це робити. GraphQL зазвичай працює через єдиний ендпоінт (POST /graphql) з різним тілом запиту, тому стандартне HTTP-кешування не застосовується напряму — потрібні спеціалізовані клієнтські бібліотеки (Apollo Client, Relay) зі своєю логікою кешування на основі нормалізованого графа даних.
Проста CRUD-логіка без складних вкладених зв'язків.
Потрібне просте кешування на рівні HTTP (CDN, браузер).
Команда вже має усталені REST-конвенції та інструменти.
Публічне API для зовнішніх розробників — REST залишається значно звичнішим і легшим для швидкого старту.
Багато різних клієнтів із різними вимогами до даних (web, iOS, Android, партнери).
Глибоко вкладені, пов'язані сутності, які інакше вимагали б багатьох REST-запитів.
Команда готова інвестувати в схему, резолвери та навчання GraphQL.
Вважати GraphQL універсально «кращим» за REST — це компроміс, а не покращення без недоліків: складніше кешування, складніша обробка помилок (GraphQL часто повертає HTTP 200 навіть при помилці всередині відповіді) і вища крива навчання.
Переносити проблему N+1 запитів у резолвери GraphQL — якщо резолвер поля posts робить окремий запит до бази даних для кожного користувача у списку, GraphQL-запит із 100 користувачами породжує ту саму проблему N+1, що й у REST-контролері (див. статтю «N+1 Query Problem»); типове рішення — DataLoader на рівні резолверів.
Проєктувати REST-ендпоінти як виклики процедур (наприклад, POST /getUserPosts) замість ресурсів — це втрачає переваги REST (передбачуваність, кешованість) без переваг GraphQL.
REST фіксує форму відповіді на сервері й вигравав за рахунок простоти та HTTP-кешування; GraphQL віддає контроль над формою відповіді клієнту й вигравав там, де клієнтів багато й кожному потрібен свій зріз даних. Жоден підхід не «кращий» універсально — вибір залежить від кількості й різноманітності клієнтів, важливості кешування та готовності команди інвестувати в додаткову інфраструктуру.