Головна / Статті / Порівняння шести стилів API: REST, GraphQL, WebSockets, Webhooks, gRPC, SOAP

Порівняння шести стилів API: REST, GraphQL, WebSockets, Webhooks, gRPC, SOAP

Дізнайтеся, як REST, GraphQL, WebSockets, webhooks, gRPC та SOAP вирішують різні проблеми обміну даними, а також отримайте карту прийняття рішень для вибору найкращого варіанту.

2251 слів

Більшість людей обирають REST як свій перший стиль API та потім сприймають його як універсальне рішення. Це не так. REST — це лише один із шести варіантів, а інші п’ять існують саме тому, що REST стикається з реальними обмеженнями в певних ситуаціях — живе оновлення даних, швидкі внутрішні виклики між сервісами, суворі вимоги до безпеки в корпоративних системах та гнучка структура даних. Кожен інший стиль API у цьому списку був створений для вирішення проблем, з якими має труднощі REST.

Якщо REST вже є для вас знайомим інструментом, те, що буде наведено далі, чітко покаже, коли варто змінити інструменти та чому.

Що таке API (один абзац, потім переходимо далі)

У своїй суті API знаходиться між двома системами та дозволяє їм взаємодіяти. Коли ви вводите слово „бір’яні“ у додаток для доставки їжі, результати ще не знаходяться на вашому телефоні. Ваш додаток надсилає запит на сервер компанії, а сервер відповідає відповідними даними. Правила, які регулюють цю взаємодію — як формується запит, як виглядає відповідь — це саме API. Уявіть офіціанта в ресторані: ви ніколи не йдете на кухню, щоб самі взяти їжу; ви кажете офіціанту, чого хочете, а він дбає про решту. Цей офіціант по суті є API.

Виявляється, існує шість різних „варіантів“ офіціантів, з якими ви можете зіткнутися.

REST: Стандарт та його обмеження

REST, скорочення від Representational State Transfer, працює на основі HTTP та ґрунтується на двох ключових ідеях: URL, який ідентифікує ресурс, який ви бажаєте отримати, та метод HTTP, який описує дію, яку потрібно виконати з ним.

Чотири методи охоплюють майже все: GET отримує дані, POST створює новий запис, PUT оновлює або замінює існуючий, а DELETE видаляє його. Ключовою рисою REST є відсутність стану — сервер не зберігає жодної інформації про попередні взаємодії з вами. Усі необхідні контекстуальні дані мають бути включені безпосередньо до запиту щоразу.

GET https://api.zomato.com/v1/restaurants?search=biryani
Authorization: Bearer <token>

Як тільки запит надходить, сервер перевіряє вашу ідентичність, отримує відповідні записи з бази даних та надсилає у відповідь JSON-дані.

Де це підходить: API, орієнтовані на користувачів, стандартні додатки для створення, читання, оновлення та видалення даних, а також у будь-яких ситуаціях, де клієнт та сервер чітко розділені та потребують прогнозованого, добре задокументованого контракту. REST отримав свій статус стандарту не без причини — він простий, не вимагає від сервера відстеження стану сесії, широко розуміється та базується на звичайному HTTP.

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

GraphQL: Запитуйте саме те, що вам потрібно

REST має добре відому проблему — надмірне отримання даних. Якщо ви звернетеся до кінцевої точки /user, ви можете отримати ім’я, фото профілю, вік, відділ, зарплату та ще десяток полів — хоча насправді вам потрібні були лише ім’я та фото. Іншим проявом є недостатнє отримання даних, коли один екран потребує інформації з кількох ресурсів, що змушує виконувати кілька запитів REST та об’єднувати результати на клієнті.

GraphQL вирішує обидві проблеми одночасно: єдина кінцева точка у поєднанні з мовою запитів, яка дозволяє клієнту точно вказати, які поля він хоче отримати.

# Instead of hitting /employees/123 and getting everything,
# you describe precisely what you need in the request body
query {
  employee(id: "123") {
    name
    photo
  }
}

У відповіді містяться лише ці два запитані поля — нічого зайвого. Потрібна ще й зарплата? Просто додайте її до запиту. Немає потреби створювати окрему кінцеву точку для цього.

GraphQL підтримує три типи операцій. Запит зчитує дані, виконуючи ту саму функцію, що й REST GET. Мутація записує або змінює дані, що еквівалентно POST, PUT та DELETE разом узятих. Підписка відкриває потік актуальних даних для постійних оновлень, функціонуючи приблизно як WebSockets.

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

Що в ньому не досконало: базові CRUD-сервіси, для яких прості кінцеві точки REST вже добре виконують свою роль. GraphQL додає справжню складність з боку сервера, кешування стає значно складнішим порівняно з REST, і це часто є зайвим навантаженням, коли потреби у даних стабільні та чітко визначені.

WebSockets: Постійне з’єднання

Функції реального часу виявляють фундаментальну слабкість REST. Щоб дізнатися, чи щойно надійшло нове повідомлення в чаті, клієнт на основі REST мусить постійно запитувати: «Щось нове?» — знову та знову. Якщо помножити це на мільйон одночасних користувачів, отримаємо мільйон запитів на секунду, більшість з яких отримують відповідь «ні», що є повною марною тратою ресурсів.

WebSockets повністю уникають цієї проблеми, замінюючи схему запит-відповідь на постійне двонаправлене з’єднання. Спочатку це звичайний HTTP-запит, але він містить спеціальний заголовок для оновлення:

GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade

Як тільки сервер приймає запит, це HTTP-з’єднання перетворюється на з’єднання WebSocket. З цього моменту кожна зі сторін може надсилати повідомлення іншій у будь-який момент без необхідності попереднього дозволу. Канал залишається відкритим, доки одна зі сторін навмисно його не закриє.

З’єднання WebSocket проходить через чотири різні стани: Connecting (відбувається укладення домовленостей), Open (повідомлення надсилаються в обох напрямках), Closing (розпочато закриття з’єднання) та Closed (з’єднання більше не існує). Спроба надіслати дані через вже закрите з’єднання призведе до збою сервера — це поширена помилка серед початківців.

Де це використовується: живі чати, багатокористувацькі ігри, інструменти для спільної редагування в реальному часі, такі як спільні документи, прямі трансляції спортивних результатів та сповіщення — по суті, у будь-якому випадку, коли сервер має надсилати дані без окремого запиту.

Що є недоліком: звичайне отримання даних, коли клієнт потребує інформації лише тоді, коли прямо про це запитує. Оскільки WebSockets підтримують постійно відкриті з’єднання, вони споживають ресурси сервера. Використання їх там, де підійде звичайний REST, просто марнує потужності без жодної користі.

Webhooks: Сервер зв’язується з вами

Як REST, так і WebSockets починаються з клієнта: клієнт відкриває з’єднання, надсилає запит, а сервер відповідає. Webhooks повністю змінюють цей ланцюг — замість того, щоб ви запитували сервер про оновлення, сервер звертається до вас у момент, коли трапляється щось важливе для знання.

Механізм досить простий. Ви реєструєте URL у якійсь сторонній службі та вказуєте, що робити з цим URL: наприклад, «коли оплата буде завершена, надішліть запит POST сюди». Як тільки оплата фактично буде здійснена, постачальник платежів — Razorpay, Stripe чи будь-хто інший, якого ви використовуєте — автоматично надсилає запит на ваш ендпоінт. Не потрібно жодних циклів перевірки чи постійного підтримання з’єднання. Вам просто потрібно чекати, поки надійде виклик.

# What you give Razorpay in setup:
Webhook URL: https://yourapp.com/webhooks/payment
# What Razorpay sends when payment completes:
POST https://yourapp.com/webhooks/payment
{
  "event": "payment.captured",
  "payload": { "amount": 50000, "order_id": "order_abc" },
  "signature": "sha256_hash_here"
}

Перевірка підпису тут не є факультативною — це вся система безпеки. Ваш кінцевий пункт webhook є доступним для загального користування, що означає, що теоретично будь-хто може надіслати фальшиву подію „payment.captured“ та обманом змусити вашу систему скасувати замовлення, за яке насправді ніхто не оплатив. Підпис, включений у вантаж даних, — це криптографічний хеш, який підтверджує, що запит справді надійшов від постачальника. Ваш сервер повинен перевірити цей підпис перед тим, як почати обробку будь-яких даних у тілі запиту.

Коли його використовувати: підтвердження оплат, зміни статусу замовлень, конвеєри CI/CD (GitHub повідомляє ваш сервер щоразу, коли надходить новий код), а також у будь-якому процесі, де потрібно реагувати на подію, що відбулася у зовнішній системі.

Коли не варто його використовувати: у будь-яких ситуаціях, що вимагають миттєвої відповіді під час тієї самої взаємодії з користувачем. Webhooks працюють пізніше — вони за своєю природою є асинхронними. Коли користувач зараз сидить перед екраном та чекає підтвердження, REST залишається кращим варіантом.

gRPC: швидкість бінарних даних для внутрішніх сервісів

Більша за розміром додаток рідко є одним сервером. Наприклад, платформа типу Zomato має окремі сервіси для обробки замовлень, оплат, сповіщень та даних про ресторани, і ці сервіси взаємодіють один з одним тисячі разів на секунду. Якщо вся ця внутрішня взаємодія відбувається через REST, доводиться постійно серіалізувати та десеріалізувати JSON. Читабельність JSON чудова для розробника, який переглядає журнали подій, але ця сама читабельність супроводжується значними витратами на обробку при великих об’ємах даних.

gRPC, спочатку створений Google для обробки власного внутрішнього трафіку, замінює JSON на Protocol Buffers (Protobuf) — бінарний формат, який є значно компактнішим та швидкішим у кодуванні та декодуванні. Той самий вміст, який REST надсилає у вигляді читабельного тексту, gRPC передає у вигляді щільного бінарного блоку, який машини обробляють набагато швидше.

// You define your data structure once in a .proto file
message OrderRequest {
  string order_id = 1;
  string user_id = 2;
  float amount = 3;
}

Покращення продуктивності залежить не лише від формату даних. gRPC також працює на основі HTTP/2, що дозволяє мультиплексацію — тисячі запитів можуть передаватися одночасно через одне спільне з’єднання, на відміну від HTTP/1.1, який обробляє їх по одному. Крім того, gRPC пропонує чотири різні схеми комунікації: Унарна (один запит разом із одною відповіддю, у тому ж форматі, що й REST), Стрімування з боку сервера (один запит, який ініціює потік відповідей, що корисно для таких цілей, як відстеження замовлень у реальному часі), Стрімування з боку клієнта (багато запитів об’єднуються в одну кінцеву відповідь, що корисно для завантаження файлу частинами) та Двостороннє стрімування (обидві сторони постійно передають дані одна одній, що підходить для функцій співпраці в реальному часі).

Коли його використовувати: для обміну даними між сервісами в межах вашої власної інфраструктури, де важливі швидкість та сувора типізація. Там, де ваші сервіси обмінюються великою кількістю запитів, а парсинг JSON стає значним фактором витрат.

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

SOAP: суворий, деталізований та все ще використовуваний у банках

SOAP (Simple Object Access Protocol) був створений ще у 1998 році, тож він старший за сам REST. Більшість сучасних розробників стикаються з ним лише під час підключення до банківських систем, страхових платформ чи великого корпоративного програмного забезпечення — галузей, які рано прийняли SOAP та так і не мали серйозних причин для переходу на інші технології.

SOAP не є гнучким. Кожне повідомлення — це XML, запакований у суворо визначену оболонку. Тоді як REST залишає багато свободи у формуванні даних, SOAP вимагає, щоб обидві сторони дотримувалися точно визначеної схеми.

<!-- Every SOAP message follows this envelope structure -->
<Envelope>
  <Header>
    <Security><!-- authentication goes here --></Security>
  </Header>
  <Body>
    <GetAccountBalance>
      <AccountId>ACC123</AccountId>
    </GetAccountBalance>
  </Body>
</Envelope>

Ця складна структура існує навмисно. Стандарт WS-Security SOAP об’єднує автентифікацію, цифрові підписи та шифрування в одне повідомлення. Для фінансових транзакцій, де будь-які зміни під час передачі можуть спричинити серйозну шкоду, цей вбудований рівень захисту виправдовує додаткову вагу структури.

Коли його використовувати: для підключення до API банку, платіжного шлюзу, який вимагає SOAP, державних систем, платформ страхування чи будь-якої старішої корпоративної системи, яка надає лише інтерфейс SOAP. Навряд чи ви оберете SOAP для проекту, який створюєте з нуля, але розуміння цього формату є важливим, коли потрібно взаємодіяти з системами, побудованими на ньому.

Коли не використовувати: у будь-якому новому проекті, де ви контролюєте обидві сторони взаємодії. SOAP вимагає більше часу для реалізації, його XML-дані ускладнюють дебаггінг, і він не має переваг перед REST чи gRPC, коли компатibilitет із старими системами більше не є обмеженням.

Карта прийняття рішень

Використовуйте це як швидкий довідник для вибору правильного інструменту:

Стандартні веб-додатки чи API, призначені для роботи з користувачами, використовують REST. Мобільні додатки, яким потрібні гнучкі дані, адаптовані під конкретні формати, використовують GraphQL. Для живого чату, багатокористувацької взаємодії чи реальних повідомлень у режимі реального часу потрібні WebSockets. Підтвердження оплат та запуск процесів CI/CD вимагають використання Webhooks. Внутрішні мікросервіси, яким потрібна висока продуктивність, використовують gRPC. Банківські системи та інтеграції зі старими корпоративними рішеннями використовують SOAP.

Що ви тепер розумієте

REST залишається стандартним вибором. Усі інші патерни існують для усунення конкретних недоліків REST: GraphQL використовується тоді, коли потреби у даних відрізняються у різних клієнтах; WebSockets — коли з’єднання має залишатися відкритим у обох напрямках; Webhooks — коли потрібно реагувати на події, замість постійних запитів про них; gRPC — коли JSON стає занадто повільним для внутрішнього обміну даними; SOAP — коли вимоги до безпеки корпоративного рівня не залишають інших варіантів.

Наступного разу, коли будете проектувати інтеграцію, не починайте з питання про те, як примусово використати REST. Натомість запитайте себе, який патерн комунікації насправді відповідає потребам системи. Саме ця відповідь має визначати інструмент, а не звичка.

Як наступний крок, виберіть один із цих шаблонів, з яким ви ще не працювали. Знайдіть його офіційну документацію або невеликий проект з відкритим кодом, створений на його основі, та уважно прочитайте приклад реальної реалізації, перш ніж вам доведеться створювати щось подібне під тиском.