Архітектура
API: контракти взаємодії між частинами системи
API (Application Programming Interface — програмний інтерфейс взаємодії) описує, як одна частина програми просить іншу виконати операцію або повернути…
API (Application Programming Interface — програмний інтерфейс взаємодії) описує, як одна частина програми просить іншу виконати операцію або повернути дані. У вебзастосунку таким інтерфейсом часто є HTTP API, але поняття API ширше за вебадресу чи окремий протокол.
У розділі бронювання буде наскрізним навчальним прикладом. Ідентифікатори, користувачі, дати, токени й результати вигадані; вони не описують реальну службу чи виміряну продуктивність.
Модель даних і транзакції визначають, що система зберігає та як захищає зміни. Коли її частини або зовнішні клієнти обмінюються запитами, потрібен контракт, який пояснює, чого кожна сторона може очікувати.
Після розділу читач може:
- відокремити контракт API від внутрішньої реалізації;
- розібрати запит, відповідь, помилку, права й межі повтору;
- оцінити сумісність зміни поля або значення;
- описати HTTP API у OpenAPI та розуміти, чого така схема не доводить;
- перевірити споживачів і відновлення, перш ніж змінювати контракт.
17.1. Контракт і обіцянка споживачеві
Контракт API — це опис доступної операції та її поведінки: хто може викликати її, які вхідні дані потрібні, що означає успіх, які помилки можливі, чи може операція виконуватися довго та чи дозволений повтор. Контракт корисний лише тоді, коли сервер реалізує описану поведінку, а споживачі знають її межі.
API не тотожне URL (Uniform Resource Locator — уніфікований локатор ресурсу). У вебзастосунку HTTP (Hypertext Transfer Protocol — протокол передавання гіпертексту) API є поширеним варіантом, але протокол і адреса все одно не описують увесь контракт. Наприклад, запит до /v1/bookings може створити бронювання одним методом, а іншим — повернути список, але саме URL не пояснює параметри, права чи наслідки.
| Частина контракту | Що команда має визначити |
|---|---|
| Операція | Яку дію дозволено виконати |
| Вхід | Поля, типи, формат, межі та значення за замовчуванням |
| Результат | Статус, поля відповіді, ідентифікатор і час завершення |
| Відмова | Код і форма помилки, яку може обробити програма |
| Доступ | Який користувач, роль або об’єкт має право на дію |
| Конкуренція | Що станеться, якщо ресурс змінився під час запиту |
| Повтор | Чи є повтор безпечним і як знайти попередній результат |
| Сумісність | Як довго старі клієнти й новий сервер працюватимуть разом |
Контракт має називати бізнес-результат. Відповідь «запит прийнято» може означати лише, що сервер поставив задачу в чергу. Якщо бронювання створюється асинхронно, клієнтові потрібні ідентифікатор операції, спосіб перевірити стан і значення станів. Інакше споживач не знає, чи може показати підтвердження клієнту.
Джерела до підглави 17.1: матеріали звірено 23 вересня 2026 року. RFC (Request for Comments — серія технічних документів Інтернету) 9110, HTTP Semantics, визначає семантику методів і відповідей; OpenAPI Specification 3.1.1 описує машинозчитуваний контракт HTTP API. Висновок про обов’язкові поля контракту — навчальний синтез; конкретні обіцянки задає власник API.
17.2. Ресурс, endpoint і метод
Ресурс — сутність, про яку API зберігає або надає відомості: слот чи бронювання. Endpoint — адреса ресурсу разом із методом запиту, наприклад GET /v1/bookings/{booking_id}. Метод описує намір споживача, але сервер має виконати відповідну семантику.
HTTP має загальні правила безпечності та ідемпотентності. Безпечний запит не просить змінити стан. Ідемпотентний запит після одного чи кількох однакових викликів має той самий задуманий ефект на ресурсі; це не обіцяє, що кожна відповідь байт у байт однакова або що сервер не записує службовий журнал.
| Метод | Типове призначення | Безпечний | Ідемпотентний за семантикою HTTP |
|---|---|---|---|
| GET | Прочитати представлення | Так | Так |
| POST | Створити або запустити дію | Ні | Зазвичай ні |
| PUT | Замінити представлення ресурсу | Ні | Так |
| PATCH | Застосувати часткову зміну | Ні | Не обов’язково |
| DELETE | Видалити ресурс | Ні | Так |
Реалізація не повинна перетворювати GET на «підтвердити бронювання»: браузер, кеш чи робот можуть повторно прочитати посилання. PUT із повторним записом того самого бажаного представлення за задумом не має створити другу бронь. PATCH може бути ідемпотентним, якщо контракт задає абсолютне значення, або не бути ним, якщо команда означає «збільшити лічильник на один». POST може підтримати безпечне повторення через окремий ключ, але HTTP сам такого захисту не додає.
У службі бронювання доречні GET /v1/slots?date=… для читання доступних слотів, POST /v1/bookings для створення бронювання й PATCH /v1/bookings/{booking_id} для дозволеної зміни. Скасування не завжди означає фізичне DELETE: якщо потрібні аудит, повернення коштів чи історія, контракт може змінити стан на cancelled окремою предметною операцією.
Джерела до підглави 17.2: матеріали звірено 23 вересня 2026 року. RFC 9110, розділ про методи, визначає safe та idempotent; MDN: HTTP request methods підсумовує їх для GET, POST, PUT, PATCH і DELETE. Таблиця вказує семантику стандарту, а не гарантує правильність конкретного сервера.
17.3. Частини запиту та форма даних
Запит може містити шлях, параметри запиту, заголовки й тіло. У відповіді сервер повертає код стану, заголовки й, за потреби, тіло. Типи цих частин мають бути явними: наприклад, ідентифікатор слота — рядок, кількість місць — ціле число, а список бронювань — масив об’єктів.
POST /v1/bookings HTTP/1.1
Host: api.example.test
Authorization: Bearer <навчальний-токен>
Accept: application/json
Content-Type: application/json
Idempotency-Key: demo-booking-8f31
{
"slot_id": "slot_demo_204",
"note": null
}
Ключ у шляху, наприклад /v1/bookings/bkg_demo_550, зазвичай ідентифікує конкретний ресурс. Параметри запиту після знака питання — наприклад ?limit=20&after=… — часто керують списком або фільтром. Заголовки передають метадані формату, автентифікації чи трасування; тіло несе дані, які сервер має перевірити. Значення заголовка не стає довіреним лише тому, що його передала програма.
JSON (JavaScript Object Notation — об’єктна нотація JavaScript) часто використовують як формат тіла. У першому запиті note передане зі значенням null. Якби поля не було, тіло виглядало б так:
{
"slot_id": "slot_demo_204"
}
Контракт має визначити, що означає відсутнє поле: застосувати значення за замовчуванням, зберегти попереднє чи відхилити запит. Це не обов’язково те саме, що null або порожній рядок; заміна відсутнього поля на порожній текст може змінити сенс. В OpenAPI 3.1 поле, яке допускає текст або null, можна описати як type: [string, "null"]; синтаксис старіших версій відрізняється, тому перед читанням схеми перевірте її версію.
Схема перевіряє форму: чи присутнє slot_id, чи рядок має дозволену довжину, чи note не перевищує ліміту. Бізнес-перевірка відповідає на інше питання: чи належить бронювання цьому клієнту, чи ще доступний слот, чи дозволяє правило змінити запис. Валідний JSON не доводить, що його зміст допустимий для продукту.
Джерела до підглави 17.3: матеріали звірено 23 вересня 2026 року. OpenAPI Specification 3.1.1 описує параметри, request body і Schema Object та використовує модель JSON Schema; JSON Schema 2020-12 визначає типи, об’єкти й масиви. Приклади запитів вигадані.
17.4. Відповідь, статус і форма помилки
Код HTTP-відповіді дає API загальну категорію результату. Контракт має узгодити його з дією:
| Код | Типовий зміст для прикладу |
|---|---|
| 200 | Запит виконано, відповідь містить представлення |
| 201 | Нове бронювання створено; відповідь може містити його ідентифікатор |
| 202 | Задачу прийнято, але її результат ще не завершений |
| 204 | Дію виконано без тіла відповіді |
| 400 | Запит не відповідає формату чи правилам API |
| 401 | Немає чинного підтвердження особи; відповідь має містити виклик автентифікації WWW-Authenticate |
| 403 | Особу розпізнано, але доступу до дії немає |
| 404 | Ресурс не знайдено або API навмисно не розкриває його існування |
| 409 | Стан ресурсу конфліктує із запитом, наприклад слот уже зайнятий |
| 422 | Тіло зрозуміле за формою, але інструкцію неможливо виконати за її змістом |
| 429 | Ліміт частоти запитів досягнуто |
| 5xx | Сервер або залежність не змогли завершити запит |
Статус успіху не завжди означає завершений бізнес-результат. Зокрема, 202 Accepted означає, що сервер прийняв запит до подальшого оброблення, а не що бронювання вже підтверджено. Для асинхронного процесу контракт може повернути operation_id і посилання на стан, а потім показувати queued, running, completed чи failed.
Відповідь на запит із 17.3 може підтвердити створення ресурсу та повернути його адресу:
HTTP/1.1 201 Created
Location: /v1/bookings/bkg_demo_550
X-Request-ID: req_demo_84a1
Content-Type: application/json
{
"id": "bkg_demo_550",
"slot_id": "slot_demo_204",
"status": "confirmed",
"created_at": "2026-09-23T12:00:00Z"
}
Статус 201 означає, що ресурс створено; Location вказує на нього. Тіло містить виданий сервером ідентифікатор, підтверджений стан і час створення. Навчальні значення вигадані.
Щоб програма не розбирала довільний текст помилки, API може використати RFC 9457, Problem Details for HTTP APIs. Нижче навчальна відповідь про конфлікт:
{
"type": "https://api.example.test/problems/slot-unavailable",
"title": "Час уже зайнятий",
"status": 409,
"detail": "Оберіть інший доступний слот.",
"instance": "/requests/req_demo_84a1",
"request_id": "req_demo_84a1"
}
У Problem Details type — URI (уніфікований ідентифікатор ресурсу), що ідентифікує тип проблеми, а title дає короткий опис; status повторює HTTP-статус, але авторитетним лишається статус самої відповіді. detail пояснює цей випадок, а instance ідентифікує його. request_id тут — додаткове поле API, корисне для пошуку в журналі, але це не доказ автентичності чи право доступу. Помилка не повинна розкривати приватні дані, внутрішній stack trace чи секрети. Різницю між 401, 403 і приватним 404 сервер має узгодити з політикою доступу (див. 17.5).
Розбір створення бронювання. Запит із підглави 17.3 надсилає POST на колекцію бронювань. Bearer-токен встановлює контекст запитувача, а сервер сам визначає його особу: клієнт не має призначати чуже бронювання полем attendee_id. Content-Type описує формат тіла, Accept — бажаний формат відповіді, а Idempotency-Key вмикає правила повтору, які має визначити сам API. slot_id вибирає слот; note: null означає «примітки немає», якщо контракт не каже інакше. Усі дані прикладу вигадані.
Відповідь 201 означає, що ресурс створено. Location дає адресу нового бронювання, id є виданим сервером ідентифікатором, а status — підтвердженим станом. У цьому прикладі X-Request-ID допомагає знайти операцію в журналі, а created_at фіксує час у UTC (Coordinated Universal Time — Всесвітній координований час). Ідентифікатор запиту корисний для підтримки, але не підтверджує особу чи право доступу.
Якщо користувач увійшов, але не має права на дію чи ресурс, сервер може повернути 403 із загальною причиною, не розкриваючи даних чужого запису:
{
"type": "https://api.example.test/problems/booking-forbidden",
"title": "Дію не дозволено",
"status": 403,
"detail": "У вас немає права виконати цю операцію.",
"instance": "/requests/req_demo_84a2"
}
403 означає заборону доступу, а 409 — конфлікт стану, наприклад зайнятий слот. За невірного або відсутнього токена зазвичай повертають 401 із WWW-Authenticate, наприклад зі схемою Bearer. Узгоджена з приватністю політика може використати 404, щоб не розкривати існування чужого ресурсу.
Правило відновлення залежить від відповіді: виправте запит після 400 — повтор незмінної невалідної дати не допоможе; перечитайте доступність після 409 і запропонуйте новий слот; після 429 дотримуйтеся Retry-After. Якщо результат неясний через тайм-аут чи 5xx, спершу з’ясуйте стан за ключем операції, а не припускайте, що сервер нічого не зробив (детально в 17.8).
Джерела до підглави 17.4: матеріали звірено 23 вересня 2026 року. RFC 9110: коди відповіді визначає класи та семантику статусів; RFC 9457 описує машинозчитувану форму problem details для HTTP API. Точний вибір коду й тексту є рішенням контракту, якщо стандарт не задає його конкретно.
17.5. Автентифікація, авторизація й доступ до об’єкта
Автентифікація встановлює, ким є запитувач або яка служба звертається до API. Авторизація перевіряє, чи дозволена конкретна дія цьому запитувачу над конкретним ресурсом. Bearer-токен у заголовку Authorization може надати доступ до захищеного API, але він є секретом: будь-хто, хто його отримав, може спробувати ним скористатися.
Роль або scope часто визначає загальну можливість, наприклад читати бронювання. Це не замінює перевірку самого об’єкта. Якщо шлях містить /v1/bookings/bkg_731, сервер має перевірити, що поточний користувач або його організація справді може читати чи змінювати саме bkg_731. Не покладайте цю перевірку на те, що ідентифікатор важко вгадати, або що endpoint прихований від інтерфейсу.
Завершений приклад межі доступу. Марта має чинний токен і може читати власні бронювання. Вона надсилає GET /v1/bookings/bkg_demo_550 — сервер перевіряє власника запису та повертає 200. Потім Марта замінює останній ідентифікатор на bkg_demo_731, який належить Олегові. Сервер має відхилити цю дію або за узгодженою політикою відповісти 404, не розкривши існування чужого запису. Перевірка «токен чинний» сама по собі не дозволяє повернути Олегове бронювання.
У навчальних прикладах використовуйте текстові заглушки на кшталт <навчальний-токен>, а не реальний ключ. Токен не кладіть у URL, знімок екрана, журнал чи повідомлення помилки. Дозволи на запис, межі організації, зміну чужого запису й адміністративні ролі мають бути частиною серверного контракту.
Джерела до підглави 17.5: матеріали звірено 23 вересня 2026 року. RFC 6750, використання bearer-токенів, описує передавання такого токена через заголовок Authorization і його ризики; Open Worldwide Application Security Project (OWASP), API Security Top 10 2023, API1: Broken Object Level Authorization, пояснює перевірку прав на кожен об’єкт, до якого звертається API. Навчальні ідентифікатори та користувачі вигадані.
17.6. Списки, фільтри й сторінки
Для списків контракт має задати дозволені фільтри, порядок, максимальний розмір відповіді та спосіб перейти до наступної сторінки. Необмежений запит «поверни всі бронювання» може перетворитися на велику передачу даних або надто дорогу операцію. Фільтр також не звільняє сервер від перевірки прав на кожен результат.
Пагінація за offset і limit зрозуміла:
GET /v1/bookings?limit=20&offset=40&order=created_at
Але якщо між сторінками перед першим рядком вставили новий запис, позиції зміщуються: наступна відповідь може повторити запис або пропустити один. Цей ризик особливо помітний у списку, що часто змінюється.
Курсорна пагінація передає непрозорий continuation token:
GET /v1/bookings?limit=20&after=opaque_cursor_a9f3k2
Сервер може продовжити після останнього ключа сортування. Для відтворюваного порядку недостатньо сортувати за неунікальною датою; додайте сталий tie-breaker, наприклад created_at разом із booking_id. Однак курсор сам собою не створює знімок даних. Якщо фільтроване поле змінюється між запитами, запис може перейти через межу вибірки; новий запис із ключем після курсора може потрапити до наступної сторінки. Якщо потрібне незмінне представлення на всіх сторінках, контракт має задавати snapshot token, версію вибірки чи інший перевірений механізм.
Запит наступної сторінки має зберегти ті самі фільтри й порядок сортування, а сервер знову перевіряє права користувача. Курсор не є токеном доступу.
Завершений приклад. Оператор переглядає бронювання за тиждень, відсортовані за часом створення й ідентифікатором. Перша відповідь містить 20 записів та next_cursor. Коли клієнт просить наступну сторінку, сервер зберігає ті самі фільтри, перевіряє користувача знову й продовжує після пари ключів. Якщо нове бронювання має пізніший час, воно може потрапити в наступну сторінку — API має описати цю властивість. Курсор не є токеном доступу: підроблений або викрадений курсор не скасовує авторизацію.
Джерела до підглави 17.6: матеріали звірено 23 вересня 2026 року. Google, настанова Pagination, вимагає визначити розмір і токен сторінки, наголошує, що токен не є правом доступу, та описує взаємодію параметрів сторінки. Правило складеного сортування й приклад зміни списку є навчальним поясненням; сервер має документувати власну поведінку.
17.7. Версії та сумісність контракту
Сумісність має щонайменше три виміри: старий клієнт може завантажити бібліотеку, надіслати запит правильної форми й далі отримати очікуваний бізнес-сенс. Одна лише назва /v1 не гарантує жодного з них. Контракт може мати поле того самого типу, але змінити його одиницю, округлення чи значення — це вже може зламати клієнта.
Повний приклад зміни price. У першій версії бронювання повертає:
{
"price": 19.9,
"currency": "EUR"
}
Клієнт трактує price як JSON-число й обчислює підсумок. Якщо новий сервер замінить його на рядок "19.90", суворий клієнт може відхилити відповідь, а інший — з’єднати текст із числом і показати помилку. Те, що JSON допускає обидва типи, не робить їх сумісними.
Варіанти переходу залежать від споживачів і правила грошей. Команда може додати нове поле money з явно визначеним форматом, тимчасово повертати старе price у старій версії, перевести клієнтів і лише після перевірки оголосити старе поле застарілим. Або випустити нову major-версію контракту та підтримувати обидві версії під час міграції. Для грошового значення треба окремо визначити точність, валюту, округлення й одиницю. Число мінорних одиниць не універсальне для всіх валют; рядок десяткового значення теж потребує задокументованого формату.
Додавання необов’язкового поля часто сумісне для клієнта, який ігнорує невідомі поля, але ламає суворий декодер, що відхиляє незнайому назву. Додавання нового обов’язкового поля до запиту ламає старих клієнтів, які його не надсилають. Зміна переліку status може зламати код із вичерпним switch. Спочатку з’ясуйте реальну поведінку споживачів, а потім обирайте зміну.
План переходу має назвати старих споживачів, дату чи умову завершення підтримки, метрику використання, поведінку після припинення версії й шлях відкату. Версіонування — це спосіб організувати несумісні контракти, а не дозвіл не повідомляти споживачів.
Джерела до підглави 17.7: матеріали звірено 23 вересня 2026 року. Google, настанова зі зворотної сумісності, розрізняє source, wire і semantic compatibility та називає зміну типу поля несумісною; OpenAPI Specification 3.1.1 визначає типи й описи поля у схемі. Приклад ціни навчальний: точне представлення грошей визначає конкретний API.
17.8. Тайм-аути, ліміти, повтори й ключ ідемпотентності
Тайм-аут означає, що клієнт перестав чекати; він не доводить, що сервер нічого не зробив. POST /v1/bookings міг створити ресурс, а відповідь загубилася мережею. Новий запит без перевірки може створити друге бронювання.
Приклад невизначеного результату. Клієнт надсилає створення бронювання з ключем idempotency-key-demo-552. Сервер у межах однієї локальної транзакції резервує слот і зберігає результат під цим ключем. Відповідь 201 губиться. Клієнт не знає, чи дія відбулася, тому повторює той самий логічний запит з тим самим ключем, тим самим клієнтом і тими самими параметрами. За контрактом сервер повертає раніше збережений ідентифікатор і не створює другого бронювання.
Ключ ідемпотентності не є загальним стандартним заголовком HTTP. Контракт має визначити, в межах якого клієнта чи операції ключ унікальний, скільки часу його зберігати, як поводитися з повтором із відмінним тілом і що відбувається після завершення строку зберігання. Такий конфлікт слід відхилити, а не мовчки виконати нову дію. Локальний ключ не гарантує одноразового ефекту у зовнішньому платіжному сервісі: потрібні його власні правила ідемпотентності та перевірка результату.
Реакція залежить від відповіді. Після 400 через неправильну дату виправте запит; після 409 перечитайте доступність замість повтору старого слота. Після 429 дотримуйтеся Retry-After; якщо заголовка немає, застосовуйте документовану обмежену затримку зі зростанням і випадковим розкидом. Обмежте кількість спроб і загальний час операції. Відмова через права, валідацію чи платіжний ліміт не є тимчасовою помилкою для сліпого повтору. Тайм-аут або 5xx теж не доводить, що зміна не відбулася: знайдіть попередній результат за ключем, перш ніж надсилати дію знову, а фінальний стан зробіть видимим клієнтові чи оператору.
Джерела до підглави 17.8: матеріали звірено 23 вересня 2026 року. RFC 9110, ідемпотентні методи, пояснює, коли клієнт може безпечно повторити HTTP-запит; Stripe: Idempotent requests документує власні правила збереження відповіді для ключа; OpenAI API: Error codes показує, що частина помилок 429 потребує змінити обмеження, а не повторювати ту саму спробу. Поведінка Stripe чи OpenAI не є універсальним контрактом інших провайдерів.
17.9. Webhook: подія між сервісами
Webhook — це HTTP (Hypertext Transfer Protocol — протокол передавання гіпертексту) повідомлення, яке сервер-джерело надсилає після події, наприклад після підтвердження оплати. Приймач перевіряє підпис за секретом провайдера на тих самих байтах тіла, які підписував відправник, і порівнює його способом, стійким до вимірювання часу. Ідентифікатор у заголовку сам по собі не доводить походження запиту.
Приклад повторної доставки. Провайдер надсилає payment_succeeded з event_id evt_demo_44. Після перевірки підпису сервіс в одній локальній транзакції реєструє подію та змінює стан платежу. Ключ дедуплікації має містити простір провайдера й ідентифікатор події. Якщо процес упав до фіксації, не збереглися ані реєстр, ані зміна платежу, тож повтор може безпечно виконати локальну транзакцію. Після фіксації сервіс швидко повертає успіх; окремі сповіщення передає через outbox (механіку розглянуто в 16.13). Якщо процес упаде до листа, завдання залишиться в журналі вихідних подій для фонового працівника після перезапуску.
Не надсилайте лист чи не зараховуйте платіж, а вже потім записуйте, що подію бачили: аварія між зовнішнім ефектом і фіксацією дедуплікації може повторити цей ефект. Так само не відповідайте 2xx до надійного збереження події, інакше збій може втратити її назавжди. Якщо відповідь губиться, провайдер повторить доставку; узгодьте його правила порядку, кількості та строку повторів, бо поведінка різних webhook API відрізняється.
GitHub радить перевіряти X-Hub-Signature-256: HMAC (Hash-based Message Authentication Code — код автентифікації повідомлення на основі хеш-функції), обчислений алгоритмом SHA-256 (Secure Hash Algorithm 256), та порівнювати підпис без витоку через час виконання. X-GitHub-Delivery лишається тим самим під час ручного повтору; він ідентифікує подію, а не окрему спробу доставки. GitHub Docs задокументував однаковий GUID (Globally Unique Identifier — формат глобально унікального ідентифікатора) у різних GitHub Apps. Якщо приймаєте події від кількох Apps або webhook-конфігурацій, додайте до простору ключа ідентичність App чи hook; сам delivery ID не обов’язково унікальний в усій організації.
Джерела до підглави 17.9: матеріали звірено 23 вересня 2026 року. GitHub: best practices for using webhooks описує строк відповіді й повторну доставку; перевірка webhook-підпису рекомендує цей алгоритм і захищене порівняння; issue github/docs#32822 і злитий pull request #33184 фіксують уточнення семантики X-GitHub-Delivery. Висновок про складений простір дедуплікації є рекомендацією для системи з кількома інтеграціями, а не універсальною гарантією GitHub.
17.10. OpenAPI описує форму, але не всю поведінку
OpenAPI Specification — специфікація опису API без прив’язки до мови програмування. Її документ може описати операції, типи, параметри, відповіді й механізми безпеки; генератор здатний використати опис для документації чи клієнтського коду. Проте сама схема не реалізує сервер, перевірку власника бронювання, конкурентний захист слота чи ідемпотентність зовнішнього платежу.
Невеликий скорочений приклад операції створення:
openapi: 3.1.1
info:
title: Навчальний API бронювань
version: "1.0.0"
paths:
/v1/bookings:
post:
operationId: createBooking
summary: Створити бронювання для поточного користувача
security:
- bearerAuth: []
parameters:
- in: header
name: Idempotency-Key
required: false
schema:
type: string
minLength: 1
description: Повторюйте той самий ключ і ті самі дані згідно з контрактом.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateBooking"
responses:
"201":
description: Бронювання створено
headers:
Location:
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Booking"
"401":
description: Токен відсутній або недійсний; сервер надсилає challenge для автентифікації
headers:
WWW-Authenticate:
description: Наприклад, Bearer challenge, якщо API використовує bearer-токен
schema:
type: string
"403":
description: Поточному користувачу заборонена ця дія
"409":
description: Слот уже зайнятий
content:
application/problem+json:
schema:
$ref: "#/components/schemas/Problem"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
CreateBooking:
type: object
additionalProperties: false
required: [slot_id]
properties:
slot_id:
type: string
note:
type: [string, "null"]
maxLength: 500
Booking:
type: object
required: [id, slot_id, status, created_at]
properties:
id:
type: string
slot_id:
type: string
status:
type: string
enum: [confirmed, cancelled]
created_at:
type: string
format: date-time
Problem:
type: object
required: [type, title, status]
properties:
type:
type: string
format: uri
title:
type: string
status:
type: integer
detail:
type: string
Перш ніж передати такий файл споживачам, звірте його з реалізацією й прикладами. Ключ ідемпотентності тут необов’язковий, а його область дії, строк зберігання й поведінка при повторі з іншим тілом не визначені; додайте ці правила, якщо операція обіцяє захист повтору. security описує механізм автентифікації, але не перевіряє власника конкретного бронювання. Відповідь 409 описує конфлікт, але не гарантує атомарного резервування слота. Поле Location має відповідати створеному ресурсу; схема Problem не перелічує всі необов’язкові поля стандарту. Генератор клієнта перевіряє частину форм, але не доводить, що сервер виконує бізнес-умови.
Джерела до підглави 17.10: матеріали звірено 26 вересня 2026 року. OpenAPI Specification 3.1.1: структура й механізми безпеки є нормативним описом формату; RFC 9457 визначає problem details. YAML є навчальним і не є перевіреною специфікацією реального сервісу.
17.11. Межі сервісів
Новий endpoint сам собою не створює корисної межі сервісу. Якщо кожна дрібна дія потребує синхронного виклику сусіда, мережеві залежності ростуть; надто широкий endpoint прив’язує клієнтів до внутрішніх деталей. Для визначення гранулярності запитайте, яка частина системи володіє бронюванням, ціною чи сповіщенням і який результат інший сервіс може вважати достатнім.
Якщо клієнту потрібні кілька послідовних викликів, визначте часткові результати: перший міг вдатися, а другий — відмовити. Мережева межа не перетворює їх на одну транзакцію; зафіксуйте власника канонічного стану, правила повтору й відновлення (надійну передачу подій розглянуто в 16.13). Це робочі компроміси між зв’язністю та розподілом відповідальності, які варто розглядати разом із моделлю даних.
Ці питання пов’язують проєктування API з архітектурою системи. У Software Architecture: The Hard Parts розглядаються компроміси між гранулярністю, зв’язністю та розподіленою відповідальністю; Designing Data-Intensive Applications, 2nd Edition дає ширший контекст для серіалізації, еволюції схем, повторів і невизначеності мережевої взаємодії. Це бібліографічні орієнтири для подальшого читання, а не твердження, що ми перевірили конкретні сторінки чи розділи повних текстів.
Джерела до підглави 17.11: бібліографічні відомості й сторінки видавця звірено 26 вересня 2026 року: Software Architecture: The Hard Parts; Designing Data-Intensive Applications, 2nd Edition — O’Reilly; сайт книги; сайт Martin Kleppmann. Повні тексти книг не використовувалися.
17.12. Два поточні API моделей — два окремі контракти
Порівнюючи API OpenAI й Anthropic, спершу прочитайте документацію кожного провайдера окремо. Схожі поняття на кшталт «вхід» чи «повідомлення» не означають однакових полів, станів, заголовків або гарантій.
Офіційна документація OpenAI описує створення відповіді Responses API через POST /v1/responses. У запиті є, зокрема, model та input; у відповіді — ідентифікатор, статус і масив результатів. Нижче скорочена форма, а не повний JSON-схема API:
{
"model": "<ідентифікатор-моделі>",
"input": "Поясни стан бронювання bkg_demo_550"
}
Документація Anthropic описує Messages API на POST /v1/messages. Її приклад містить версійований заголовок anthropic-version, ключ у заголовку x-api-key і поля model, max_tokens та messages:
POST /v1/messages HTTP/1.1
Host: api.anthropic.com
anthropic-version: 2023-06-01
x-api-key: <навчальний-заглушений-ключ>
Content-Type: application/json
{
"model": "<ідентифікатор-моделі>",
"max_tokens": 512,
"messages": [
{
"role": "user",
"content": "Поясни стан бронювання bkg_demo_550"
}
]
}
Ці два фрагменти не визначають однаковий формат відповіді, набір інструментів або правила зберігання. Не переносіть параметр із одного провайдера в інший без перевірки його актуального контракту. У документації Anthropic окремо описана політика версії заголовка; приклад відповіді OpenAI має власний набір status та output. У прикладах ключ — лише заглушка, і жоден секрет не має потрапити до книги чи коду.
API моделі додає зовнішню залежність із власними помилками, лімітами й умовами зберігання. Ваш застосунок усе одно має контролювати доступ, тайм-аут, повтор, журналювання та відповідь користувачеві. Текст моделі не є доказом, що бізнес-дію виконано; для цього перевіряйте результат вашої операції або стан, повернений самим API.
Джерела до підглави 17.12: матеріали звірено 26 вересня 2026 року. OpenAI API Reference: Create a model response описує Responses API, вхід та структуру відповіді; Anthropic API Reference: Create a Message показує endpoint, заголовки й тіло запиту; Anthropic: API versioning пояснює обов’язковий заголовок версії та допустимі зміни всередині версії. Це приклади конкретних платформ, а не універсальний стандарт.
17.13. Адаптація для інструментів агента
Адаптація для агентного інструмента, а не гарантія моделі. Схема інструмента описує назву, аргументи й типи. Модель може запропонувати один чи кілька викликів, а застосунок виконує їх і передає результат назад. Це пропозиція, не дозвіл: сервер перевіряє аргументи, право й інваріант так само, як для звичайного клієнта API.
Наприклад, замість інструмента execute_sql(text) використайте обмежену операцію confirm_booking(booking_id, expected_version). Обробник сам визначає користувача із захищеного контексту, перевіряє право на об’єкт, перевіряє версію та підтверджує бронювання в транзакції. Схема, яку прочитала модель, не є авторизацією; навіть коректний booking_id може належати іншій людині.
Для тривалої дії віддавайте окремий operation_id та стан. Після тайм-ауту інструмент має допомогти перевірити попередній результат, а не змусити агента вгадати, чи можна повторити. Дозвольте лише потрібні конкретному завданню операції, обмежте обсяг відповіді та не повертайте токени, приватні поля чи довільний дамп бази. Перед значущою зовнішньою дією власник продукту може вимагати підтвердження людини.
Джерела до підглави 17.13: матеріали звірено 26 вересня 2026 року. OpenAI: Function calling описує багатокроковий цикл між моделлю, застосунком і викликом інструмента; Anthropic: Create a Message документує параметри tools і input_schema. Запобіжні правила цього підрозділу адаптують звичайну серверну авторизацію та валідацію, а не є гарантіями поведінки моделі.
17.14. Перед зміною API
Перед зміною знайдіть актуальний опис операції, приклади й реальних споживачів: клієнти, інтеграції, агенти, фонові задачі та партнери. Визначте, чи змінюються лише форма даних, чи також бізнес-сенс; перевірте, як працюватимуть старі й нові версії разом і які помилки чи невизначені результати може побачити клієнт.
Назвіть умову, за якої стару версію можна безпечно припинити, як помітити невдалий перехід і як його відкотити: використання версій, помилки, тривалість і пошук операції за request ID. Звірте OpenAPI з реалізацією та відповідями сервера; для інструментів агента перевірте вузькість дозволу й можливість з’ясувати результат після перерваного виклику.
Якщо даних про споживачів немає, зафіксуйте цю невідомість як ризик і спершу зберіть свідчення. Не називайте зміну сумісною лише тому, що сервер компілюється або OpenAPI файл проходить перевірку схеми.
- Знайти актуальний опис операції та приклади, а не припускати поведінку за назвою endpoint.
- Перелічити вебклієнти, мобільні застосунки, інтеграції, агенти, фонові задачі й партнерів, які читають або надсилають поле.
- Відрізнити зміну форми від зміни бізнес-сенсу: чи змінюється тип, одиниця, default, перелік станів, право, порядок або час завершення?
- Перевірити невдалі випадки: відсутнє поле, null, чужий object ID, дублікат, конфлікт, тайм-аут після фіксації та повторний запит.
- Визначити, які старі й нові версії можуть працювати одночасно, як відстежити споживачів і коли безпечно припинити старий варіант.
- Звірити текст OpenAPI з серверною реалізацією й прикладами відповідей; генерацію клієнта не вважати перевіркою всіх бізнес-умов.
- Описати спостережуваність: request ID, версію контракту, код помилки, тривалість та безпечний спосіб знайти невизначену операцію.
- Для агентного використання перевірити, що інструмент має вузький дозвіл, параметри перевіряються сервером і результат можна з’ясувати після перерваного виклику.
Джерело до підглави 17.14: Google AIP-180 розрізняє сумісність джерела, wire-формату й семантики. Це робоча перевірка для команди, а не універсальний сертифікат сумісності.
Підсумок
API — це контракт про операції, дані, помилки, права й повтори. Метод HTTP задає загальну семантику, але не реалізує бізнес-правило. Схема показує допустиму форму, але не підтверджує право на бронювання. Статус описує результат запиту, але тайм-аут може залишити клієнта без знання про фактичну зміну.
Надійний контракт дозволяє споживачеві відрізнити відмову від невизначеного результату, повторити лише те, що безпечно повторювати, і змінити клієнт без прихованого зламу. Перед релізом команда перевіряє реальних споживачів, права на кожен об’єкт, сумісність значень і шлях відновлення після часткового збою.
Джерела та подальше читання
- IETF: RFC 9110 — HTTP Semantics (2022), RFC 6750 — Bearer Token Usage (2012), RFC 9457 — Problem Details for HTTP APIs (2023).
- OpenAPI Specification 3.1.1 та Google AIP-158 Pagination і AIP-180 Backwards Compatibility; динамічні сторінки звірено зазначеної вище дати.
- Додаткові первинні довідники: MDN HTTP request methods, JSON Schema Draft 2020-12 та OpenAI Function calling; сторінки перевірено 23 вересня 2026 року.
- OWASP: API Security Top 10 2023, API1: Broken Object Level Authorization.
- GitHub: Webhook delivery best practices, signature validation, issue github/docs#32822 і злитий PR #33184.
- Stripe: Idempotent requests; правила Stripe стосуються саме Stripe API.
- Поточні контракти платформ: OpenAI API errors, Responses API, Anthropic Messages API і Anthropic API versioning; сторінки звірено 23 вересня 2026 року.
- Книжкові джерела: Designing Data-Intensive Applications, 2nd Edition (Martin Kleppmann і Chris Riccomini, O’Reilly, лютий 2026) та Software Architecture: The Hard Parts (Neal Ford та співавтори, видання, зазначене на сторінці автора). Доступ до повного тексту цих книг не підтверджувався; використовувалися бібліографічні сторінки й доступні матеріали, а не неперевірені книжкові цитати.