Уявіть ситуацію: ви розробляєте мобільний застосунок для замовлення кави. Користувач натискає кнопку «Замовити лунго», і... що відбувається далі?
Десь на сервері є база даних із рецептами, кавовими машинами, цінами, поточними замовленнями. Ваш мобільний застосунок не має прямого доступу до цієї бази — і це правильно. Якби кожен телефон мав прямий доступ до серверної бази даних, це було б:
Потрібен посередник — чітко визначений контракт, який каже: «Ось що ти можеш запитати, ось у якому форматі, і ось що ти отримаєш у відповідь». Цей контракт називається API.
Що ви дізнаєтесь?
Пререквізити
API (Application Programming Interface, програмний інтерфейс застосунку) — це контракт між двома програмними компонентами, який визначає:
Важливо розуміти: API — це не реалізація, а специфікація. Меню ресторану не змінюється від того, чи працює на кухні шеф-кухар або стажер. Так само, API визначає контракт взаємодії, а не те, як саме сервер виконує запит всередині.
Поширена помилка — ототожнювати API з переліком ендпоінтів (URL-адрес). Але API — поняття значно ширше:
| Аспект | Просто URL-адреси | Повноцінний API |
|---|---|---|
| Формат даних | Не визначений | JSON, XML, Protobuf — чітко задані |
| Обробка помилок | Довільна | Стандартизовані коди та формати |
| Версіонування | Відсутнє | Продумана стратегія сумісності |
| Документація | Немає | OpenAPI/Swagger специфікація |
| Гарантії | Жодних | SLA, ліміти, політика кешування |
| Безпека | Не продумана | Аутентифікація, авторизація, rate limiting |
Саме тому дизайн API — це окрема інженерна дисципліна, якій присвячено цей модуль.
Клієнт-серверна архітектура — це фундаментальний паттерн побудови розподілених систем, де компоненти поділяються на дві ролі:
Ключовий принцип цієї моделі: клієнт і сервер не знають внутрішнього устрою один одного. Клієнт не знає, чи сервер написаний на C#, Python чи Go. Сервер не знає, чи клієнт — це браузер, мобільний додаток чи скрипт у терміналі. Вони спілкуються виключно через контракт API.
Ця незалежність має величезні практичні наслідки:
🔄 Незалежна еволюція
📱 Мультиплатформність
⚡ Масштабування
🧪 Тестування
Не всі API однакові. Залежно від того, хто є споживачем API, змінюються вимоги до його дизайну, документації та безпеки.
Використовується тільки всередині організації — для взаємодії між мікросервісами, внутрішніми інструментами, бекенд-компонентами.
// Внутрішній API для сервісу повідомлень
// Доступний лише в приватній мережі
var app = WebApplication.Create(args);
app.MapPost("/internal/notifications/send",
(NotificationRequest req) =>
{
// Немає зовнішньої автентифікації —
// довіряємо мережевому контуру
return Results.Ok(new { sent = true });
});
app.Run();
Характеристики:
Надається обмеженому колу зовнішніх компаній-партнерів за договором.
// Партнерське API для B2B інтеграції
var app = WebApplication.Create(args);
app.MapGet("/v1/partners/{partnerId}/orders",
(string partnerId, HttpContext ctx) =>
{
// Перевірка API-ключа партнера
var apiKey = ctx.Request.Headers["X-Partner-ApiKey"]
.FirstOrDefault();
if (string.IsNullOrEmpty(apiKey))
return Results.Unauthorized();
// Повернення замовлень лише цього партнера
return Results.Ok(new {
partner_id = partnerId,
orders = new[] {
new { id = 1, status = "completed" }
}
});
});
app.Run();
Характеристики:
Доступне будь-якому розробнику в Інтернеті. Це те, з чим ви найчастіше будете працювати.
// Публічне API для мобільного додатку
var app = WebApplication.Create(args);
app.MapGet("/v1/recipes", () =>
{
// Будь-хто може отримати список рецептів
return Results.Ok(new[]
{
new { id = "lungo", title = "Лунго", volume = "250ml" },
new { id = "americano", title = "Американо", volume = "350ml" }
});
});
app.MapPost("/v1/orders", (OrderRequest order) =>
{
// Створення замовлення вимагає авторизації
return Results.Created($"/v1/orders/{42}", new { id = 42 });
});
app.Run();
record OrderRequest(string Recipe, string CoffeeMachineId);
Характеристики:
Термін «API» з'явився задовго до вебу. Спочатку він означав інтерфейс бібліотеки — набір функцій, які можна викликати з коду:
// Бібліотечне API — виклик функції напряму
var result = Math.Sqrt(144); // 12
З розвитком Інтернету та розподілених систем виникла потреба викликати функції на іншому комп'ютері, через мережу. Так з'явилися мережеві API (Network API або Web API).
Принципова різниця:
| Аспект | Бібліотечне API | Мережеве API |
|---|---|---|
| Виклик | Прямий (в межах процесу) | Через мережу (HTTP, gRPC) |
| Швидкість | Наносекунди | Мілісекунди (мережева затримка) |
| Надійність | Гарантована (якщо код працює) | Мережа може впасти в будь-який момент |
| Формат даних | Нативні типи мови | Серіалізація (JSON, Protobuf) |
| Версіонування | Через NuGet/npm версії | Через URL або заголовки |
Може здатися: «Навіщо окремий модуль для API? Достатньо написати кілька ендпоінтів і все працює». Але ось сценарій із реального життя:
Ви створюєте один ендпоінт: GET /orders — повертає список замовлень. Все працює.
Фронтенд-розробник просить додати фільтр за статусом: GET /orders?status=active. Додаєте — працює.
Потрібно шукати замовлення для лунго і латте одночасно. GET /orders?recipe=lungo&recipe=latte? Але стандарту передачі масивів у URL не існує. Доводиться вигадувати: recipe=lungo,latte? Чи recipe[]=lungo&recipe[]=latte? Кожен фреймворк обробляє це по-різному.
Клієнт кешував GET /orders, і тепер показує застарілі дані. Потрібна політика кешування: Cache-Control, ETag, If-None-Match.
Мережа обірвалась під час POST /orders. Клієнт повторив запит — і створив дублікат замовлення. Потрібен токен ідемпотентності.
Кожна з цих проблем має відомі та перевірені рішення. Саме їх ми й вивчатимемо в цьому модулі.
Цей модуль побудований за принципом «від простого до складного». Кожна наступна тема спирається на попередню:
Протягом усього модуля ми будемо використовувати один наскрізний приклад — API сервісу замовлення кави. Цей приклад дозволяє продемонструвати всі паттерни: від простого CRUD до складних сценаріїв синхронізації та обробки помилок.
Для кожного з наведених сценаріїв визначте, який тип API (внутрішнє, партнерське, публічне) найкраще підходить. Обґрунтуйте вибір.
Для кожного з наведених компонентів визначте, чи виконує він роль клієнта, сервера, чи обох одночасно:
Придумайте власну аналогію для API з реального світу (не ресторан/офіціант — ця аналогія вже використана). Опишіть, що в вашій аналогії є:
Оберіть один з публічних API (наприклад, GitHub API, JSONPlaceholder або OpenWeatherMap) і дайте відповідь:
::accordion-item{label="Завдання 3.1: Спроєктуйте API "з нуля"" icon="i-lucide-circle-help"} Уявіть, що ви проєктуєте API для сервісу бронювання кімнат переговорів в офісі. Визначте:
Це завдання — перший крок до створення повноцінної специфікації, яку ми навчимося писати в останній статті модуля.
::
API — це контракт
Клієнт ≠ Сервер
Тип визначає дизайн
Дизайн API — інженерна дисципліна
Далі: у наступній статті ми розберемо формати даних — JSON, XML, TOML та бінарні формати. Навчимося обирати правильний формат і розберемо стратегії стиснення для оптимізації трафіку.
Swagger / Swashbuckle у Minimal API: окремий класичний шлях
Окремий матеріал про Swagger UI та Swashbuckle в ASP.NET Core Minimal API: коли використовувати, як підключити в .NET 8, як працює AddEndpointsApiExplorer, AddSwaggerGen, UseSwagger, UseSwaggerUI та як документувати endpoint-и.
Формати даних: JSON, XML, TOML та бінарні формати
Глибокий розбір форматів передачі даних у HTTP API: синтаксис JSON, порівняння з XML та TOML, бінарні формати (Protobuf, FlatBuffers), стратегії стиснення (gzip, brotli, deflate).