Парадигми API та концепція REST
Парадигми API та концепція REST
1. Історична довідка: Як з'явились мережеві API
Виконання запитів на віддаленому сервері — одна з базових задач у програмуванні ще від часів мейнфреймів. З появою мереж ARPANET (попередника Інтернету) теоретики швидко дійшли до думки: було б зручно, якби виклик функції на віддаленому сервері з точки зору коду нічим не відрізнявся від виклику локальної функції.
Цю концепцію у 1981 році формалізував Брюс Нельсон з лабораторії Xerox PARC під назвою Remote Procedure Call (RPC) — і він же був співавтором першої практичної реалізації (Sun RPC, який існує і сьогодні під назвою ONC RPC).
RPC першого покоління: зручність ≠ продуктивність
Перші широко розповсюджені RPC-протоколи — Sun RPC, Java RMI, CORBA — дозволяли працювати з віддаленими викликами як з локальними. Вся «магія» була сховано всередині обв'язки (stub), яку генерував фреймворк.
Звучить ідеально? Але саме ця зручність стала ахіллесовою п'ятою:
🔗 Жорстка прив'язка
🐌 Складність проксіювання
📈 Проблеми масштабування
🏗️ Складність протоколу
2. Поява HTTP та зміна парадигми
Паралельно з кризою RPC першого покоління відбувається стандартизація мережевих протоколів. Стек TCP/IP стає домінуючим. Але TCP/IP має суттєвий недолік з прикладної точки зору — він оперує IP-адресами, які люди не запам'ятовують.
Зручною абстракцією стала система доменних імен (DNS), а для передачі гіпертексту Тім Бернерс-Лі у 1991 році створив HTTP 0.9 — простий протокол для отримання документів. Він дозволяв відкрити TCP-з'єднання з сервером і передати рядок на кшталт GET адреса_документа.
Згодом HTTP стрімко розвивався: з'явились нові методи (POST, PUT, DELETE), статус-коди, заголовки, типи даних. HTML еволюціонував у XML, а потім у JSON як формати для API.
HTTP виявився ідеологічно протилежним до RPC: він не передбачав ні нативної обв'язки для викликів, ні розділеного доступу до пам'яті. Натомість запропонував надзвичайно зручні концепції:
- Кешування «з коробки» через заголовки
- Прозорі проксі — проміжні вузли, що не впливають на протокол
- URL — зрозуміла та стандартизована адресація ресурсів
Переворот: від процедур до ресурсів
У середині 1990-х відбувається поступова відмова від RPC першого покоління на користь нового підходу, де стосунки між даними та операціями перевертаються з ніг на голову:
| Аспект | RPC (процедурний) | Ресурсоорієнтований (HTTP) |
|---|---|---|
| Одиниця доступу | Ім'я операції (функції) | Адреса ресурсу (URL) |
| Набір операцій | Довільний, визначений розробником | Лімітований, стандартизований (GET, POST...) |
| Стан | Може зберігатися на сервері | Клієнт і сервер принципово не мають спільного стану |
| Проміжні вузли | Ускладнюють роботу | Прозорі (прокси, гейтвеї не впливають на протокол) |
| Кешування | Кастомне рішення | Стандартне, через заголовки HTTP |
// ❌ RPC-підхід: URL описує ДІЮ
// POST /api/createOrder
// POST /api/getOrderById
// POST /api/cancelOrder
app.MapPost("/api/createOrder", (CreateOrderRequest req) =>
{
// Проблема: всі операції через POST,
// URL не відображає ресурси,
// неможливо кешувати
return Results.Ok(new { id = 42 });
});
// ✅ Ресурсоорієнтований підхід: URL описує РЕСУРС
// POST /v1/orders → створити
// GET /v1/orders/42 → отримати
// DELETE /v1/orders/42 → скасувати
app.MapPost("/v1/orders", (CreateOrderRequest req) =>
{
// Метод HTTP визначає операцію
// URL ідентифікує ресурс
// GET-запити можна кешувати автоматично
return Results.Created("/v1/orders/42", new { id = 42 });
});
3. REST: Що насправді написав Філдінг
У 2000 році один із авторів специфікації HTTP — Рой Філдінг — захистив докторську дисертацію «Архітектурні стилі та дизайн архітектури мережевого програмного забезпечення». П'ята глава мала назву «Representational State Transfer (REST)».
Шість обмежень REST
1. Клієнт-серверна архітектура
Клієнт і сервер не знають внутрішнього устрою один одного. Це забезпечує незалежну еволюцію обох сторін.
2. Stateless (без стану)
Сесія зберігається на клієнті. Кожен запит від клієнта повинен містити всю інформацію, необхідну для його обробки. Сервер не зберігає контексту між запитами.
3. Кешування
Дані повинні розмічатися як кешовані або некешовані. Це дозволяє клієнту та проміжним проксі зберігати відповіді, зменшуючи навантаження на сервер.
4. Уніфікований інтерфейс
Інтерфейси взаємодії між компонентами повинні бути стандартизовані. Саме тому HTTP визначає фіксований набір методів (GET, POST, PUT, DELETE), а не довільні процедури.
5. Багатошарова система
Мережеві системи є багатошаровими — сервер може бути лише проксі до інших серверів. Клієнт не повинен знати, чи спілкується він безпосередньо з сервером, чи з проміжним вузлом.
6. Code-On-Demand (опціонально)
Функціональність клієнта може бути розширена через поставку коду з сервера (наприклад, JavaScript у браузері).
4. Міфологія REST: Чому «REST API» — неточний термін
Ключовий висновок із визначення REST за Філдінгом-2000: практично будь-яке мережеве ПЗ у світі відповідає принципам REST, за дуже рідкісними винятками.
Справді:
- Складно уявити систему без хоч якоїсь стандартизації взаємодії (хоча б TCP/IP)
- Якщо є інтерфейс, під нього завжди можна мімікрувати → клієнт і сервер незалежні
- Якщо можна зробити альтернативну реалізацію сервера → можна поставити проксі → багатошаровість
- Клієнт — обчислювальна машина → завжди зберігає якийсь стан і кешує якісь дані
Ще більше плутанини: Філдінг-2008
У 2008 році Філдінг випустив роз'яснення, де додав ще суворіші вимоги:
- Клієнт взагалі нічого не повинен знати про API наперед
- Не повинно бути фіксованих шаблонів URL — клієнт має отримувати посилання з відповідей сервера (концепція HATEOAS — Hypermedia As The Engine Of Application State)
Що ми маємо на увазі під HTTP API
У цьому модулі HTTP API означає:
- Протоколом є HTTP версії 1.1 і вище
- Форматом даних є JSON
- Ідентифікаторами ресурсів є URL відповідно до стандарту
- Семантика HTTP-дієслів (методів) відповідає специфікації
- Жодні веб-стандарти не порушуються навмисно
var app = WebApplication.Create(args);
// URL ідентифікує ресурс: замовлення з id=42
// GET — семантика «отримати»
// Відповідь у JSON
app.MapGet("/v1/orders/{id}", (int id) =>
Results.Ok(new
{
id,
recipe = "lungo",
status = "processing"
}));
// POST — семантика «створити»
// Content-Type: application/json
app.MapPost("/v1/orders", (OrderRequest req) =>
Results.Created($"/v1/orders/42", new { id = 42 }));
app.Run();
record OrderRequest(string Recipe, string CoffeeMachineId);
5. Сучасні альтернативи: gRPC, GraphQL
Починаючи з кінця 2010-х ми спостерігаємо розквіт RPC-технологій нового покоління — або, точніше, комбінованих технологій, які одночасно:
- Зручні — поставляються з обв'язкою для кодогенерації
- Інтероперабельні — працюють поверх стандартизованих протоколів
- Масштабовані — абстрагують поняття ресурсу
gRPC
gRPC — класична технологія другого покоління від Google:
✅ Переваги
- Використовує HTTP/2 та ефективний Protobuf
- Contract-first: розробка починається зі специфікації
.proto - Кодогенерація для зручної роботи
- Висока продуктивність
❌ Недоліки
- Складність налагодження (бінарний формат)
- Слабка підтримка браузерів
- Менша поширеність → вищий поріг входу
- Потенційна залежність від Google
// coffee_service.proto
syntax = "proto3";
service CoffeeService {
// RPC-стиль: ім'я операції
rpc CreateOrder(CreateOrderRequest)
returns (OrderResponse);
rpc GetOrder(GetOrderRequest)
returns (OrderResponse);
}
message CreateOrderRequest {
string recipe = 1;
string coffee_machine_id = 2;
}
message OrderResponse {
int32 id = 1;
string status = 2;
}
GraphQL
GraphQL від Facebook — підхід, що об'єднує концепцію «ресурсів» з надзвичайно багатою мовою запитів:
# Клієнт точно вказує, які поля потрібні
query {
order(id: 42) {
id
recipe
status
machine {
location
}
}
}
Порівняння технологій
| Аспект | HTTP API (JSON) | gRPC | GraphQL |
|---|---|---|---|
| Формат даних | JSON (текстовий) | Protobuf (бінарний) | JSON (текстовий) |
| Протокол | HTTP/1.1+ | HTTP/2 | HTTP/1.1+ |
| Підхід | Ресурсоорієнтований | Contract-first RPC | Мова запитів |
| Фіксований набір полів | Так (весь ресурс) | Так (за .proto) | Ні (клієнт обирає) |
| Кешування HTTP | Стандартне | Обмежене | Складне |
| Стрімінг | SSE, WebSocket | Вбудований (bidirectional) | Subscriptions |
| Порог входу | Низький | Середній | Середній–Високий |
| Інструменти | Найбільша екосистема | Google-екосистема | Facebook-екосистема |
Принциповий висновок
Фактично, ідеологічна різниця між сучасним HTTP API та сучасним RPC зводиться до двох речей:
- Адресація: в HTTP API одиницею доступу є ресурс (а параметри операції передаються додатково), в RPC — ім'я операції (а адреси ресурсів передаються додатково)
- Кешування: HTTP API стандартно розмічає кешовані дані, RPC — як правило, ні
І головне: майже всі сучасні протоколи працюють поверх HTTP. Тому gRPC одночасно є HTTP API (в технічному сенсі використання протоколу HTTP).
6. Вибір технології: Коли що використовувати
HTTP API (JSON)
gRPC
GraphQL
7. Головна перевага HTTP API: Промежуточні агенти
Чому HTTP API вижив, коли з'явились «швидші» та «зручніші» альтернативи? Відповідь — у промежуточних агентах.
Сучасний стек взаємодії між клієнтом і сервером — багатошаровий:
Кожен із цих проміжних агентів може читати метадані HTTP (URL, заголовки, статус-коди) без розбору тіла запиту, і автоматично:
- Кешувати відповіді (CDN, проксі)
- Логувати запити (nginx access logs → Prometheus → Grafana)
- Балансувати навантаження
- Шардувати дані за URL
- Налаштовувати таймаути та retry-політики
Для RPC-протоколів, де вся інформація захована в тілі запиту, жоден із цих агентів не зможе «зрозуміти» запит без спеціальної підтримки протоколу.
8. Практичні завдання
Рівень 1: Базовий
Перепишіть наступні RPC-стилі ендпоінти у ресурсоорієнтований стиль HTTP API на Minimal API:
POST /api/getUserProfile {"user_id": 15}
POST /api/updateUserEmail {"user_id": 15, "email": "new@test.com"}
POST /api/deleteUser {"user_id": 15}
POST /api/listUserOrders {"user_id": 15, "page": 1}
Напишіть код на C# (Minimal API), використовуючи правильні HTTP-методи та URL-структуру.
Для кожного з наступних API визначте, до якої парадигми (HTTP API / gRPC / GraphQL) воно належить:
POST /graphqlз тілом{"query": "{ user(id: 1) { name } }"}GET /v1/users/1→{"id": 1, "name": "Alice"}.protoфайл ізservice UserService { rpc GetUser(...) }PATCH /v1/users/1з тілом{"name": "Bob"}POST /api/users.getProfileз тілом{"user_id": 1}
Рівень 2: Аналітичний
Ваша команда обговорює технологію для нового проєкту — платформи доставки їжі. Є три мікросервіси:
- API для мобільного додатку (замовлення, меню, відстеження)
- Внутрішній сервіс оплати (100к транзакцій/с)
- Dashboard для адміністраторів (складні звіти з багатьма зв'язками)
Для кожного сервісу оберіть технологію та обґрунтуйте.
9. Резюме
Від RPC до REST
REST — обмеження, не стандарт
HTTP API — наш термін
Альтернативи мають свою нішу
Далі: у наступній статті ми детально розберемо складові HTTP-запитів — URL-адресацію, HTTP-методи (GET, POST, PUT, DELETE, PATCH), заголовки та статус-коди відповідей. Це фундамент, на якому будується весь дизайн HTTP API.
Формати даних: JSON, XML, TOML та бінарні формати
Глибокий розбір форматів передачі даних у HTTP API: синтаксис JSON, порівняння з XML та TOML, бінарні формати (Protobuf, FlatBuffers), стратегії стиснення (gzip, brotli, deflate).
HTTP-методи, статус-коди та заголовки
Складові HTTP-запитів: URL-адресація, HTTP-методи (GET, POST, PUT, DELETE, PATCH), їх семантика та ідемпотентність, заголовки, статус-коди 1xx–5xx та антипатерни.