Пошук уроків, статей та іншого контенту
Що насправді робить API «RESTful» — не просто «API поверх HTTP», а конкретний набір принципів проєктування ресурсів.

REST API — це не просто набір HTTP-ендпоїнтів із JSON-відповідями. REST — архітектурний стиль, у якому клієнт і сервер взаємодіють із ресурсами за допомогою стандартизованого інтерфейсу.
API можна побудувати поверх HTTP і водночас порушити ключові принципи REST. Наприклад, такі маршрути:
POST /getUser
POST /createOrder
POST /deleteProductтехнічно працюватимуть, але більше нагадують RPC-інтерфейс: клієнт викликає команди. У REST зазвичай моделюють ресурси, а HTTP-методи описують операції над ними:
GET /users/42
POST /orders
DELETE /products/15REST — скорочення від Representational State Transfer. Це архітектурний стиль, описаний Роєм Філдінгом як набір обмежень для розподілених систем.
Основна ідея:
сервер надає доступ до ресурсів;
кожен ресурс має ідентифікатор;
клієнт працює з представленнями ресурсів;
HTTP використовується за своїми семантичними правилами;
кожен запит містить достатньо інформації для його обробки.
Ресурсом може бути:
користувач;
стаття;
замовлення;
товар;
коментар;
колекція товарів;
результат пошуку.
Важливо розрізняти ресурс і його представлення. Наприклад, користувач як сутність — це ресурс, а JSON-документ із полями id, name і email — одне з його представлень.
RESTful API дотримується кількох архітектурних обмежень.
Клієнт і сервер мають окремі обов’язки:
клієнт відповідає за інтерфейс користувача та стан взаємодії;
сервер відповідає за дані, бізнес-правила та їх збереження.
Завдяки такому поділу клієнт можна змінити незалежно від сервера. Наприклад, один API може використовуватися вебзастосунком, мобільним клієнтом і сторонньою інтеграцією.
Кожен запит має бути самодостатнім. Сервер не повинен покладатися на прихований стан попередніх запитів цього клієнта.
Наприклад, поганий підхід:
клієнт викликає /startSession;
сервер зберігає тимчасовий контекст;
клієнт викликає /nextStep;
сервер здогадується, про яку операцію йдеться, за даними сесії.
У безстановому API кожен запит містить необхідний контекст:
GET /orders/123 HTTP/1.1
Authorization: Bearer <token>
Accept: application/jsonБезстановість не означає, що сервер не може мати базу даних або зберігати користувачів. Ідеться саме про стан конкретної взаємодії між запитами.
Переваги безстановості:
простіше масштабувати сервер горизонтально;
будь-який екземпляр сервісу може обробити запит;
легше повторювати та тестувати запити;
менше прихованих залежностей між операціями.
REST передбачає стандартизований спосіб взаємодії з ресурсами. Клієнт не повинен знати внутрішню реалізацію сервера.
Єдиний інтерфейс зазвичай включає:
ідентифікатори ресурсів;
стандартні HTTP-методи;
зрозумілі формати представлень;
самодостатні повідомлення;
узгоджені правила помилок.
Саме це відрізняє REST від довільного HTTP API. Якщо всі операції реалізовані через POST, а назви маршрутів описують команди, HTTP використовується лише як транспорт.
Відповідь має явно або неявно визначати, чи можна її кешувати.
Для цього використовують HTTP-заголовки:
Cache-Control: public, max-age=300
ETag: "article-42-v7"Клієнт або проміжний кеш може повторно використати відповідь протягом п’яти хвилин. ETag дає змогу перевірити, чи змінився ресурс, не завантажуючи його повністю.
Кешування особливо корисне для:
публічних статей;
списків категорій;
конфігурації;
статичних або рідко змінюваних даних.
Приватні чи чутливі відповіді не слід робити загальнодоступними для кешування.
Клієнт може не знати, з яким саме компонентом він взаємодіє. Між клієнтом і сервером можуть бути:
CDN;
reverse proxy;
балансувальник навантаження;
API gateway;
сервіс авторизації;
кеш.
Кожен шар має виконувати власну роль, не змінюючи контракт API без необхідності.
REST також допускає передавання виконуваного коду від сервера до клієнта, наприклад JavaScript. Це необов’язкове обмеження, а більшість JSON API його не використовує.
Ресурс має отримати стабільний і зрозумілий ідентифікатор — URL.
Приклади:
/users
/users/42
/users/42/orders
/orders/123
/products/15/reviewsНазви ресурсів зазвичай формулюють іменниками:
GET /articles
GET /articles/42
GET /articles/42/commentsДієслово вже закладене в HTTP-методі. Тому такі маршрути менш бажані:
GET /getArticles
POST /createArticle
POST /deleteArticleЦе не означає, що кожну бізнес-операцію можна природно представити простим CRUD-ресурсом. Для складних дій можна моделювати окремий ресурс або використати дієслівний маршрут як виняток, але це рішення має бути послідовним і обґрунтованим.
Колекція позначається множиною:
/articlesОкремий елемент — ідентифікатором:
/articles/42Вкладені маршрути корисні, коли дочірній ресурс залежить від батьківського:
/articles/42/commentsАле надмірна вкладеність ускладнює API:
/companies/1/departments/2/teams/3/users/4/permissionsЯкщо ресурс має власне незалежне життя, його можна адресувати окремо:
/permissions/99Потрібно заздалегідь визначити правила:
однина чи множина;
kebab-case, snake_case або camelCase;
використання ідентифікаторів;
формат дат і часу;
правила для вкладених ресурсів.
Наприклад, варто обрати один стиль і не змішувати:
/user-profiles
/order-itemsабо:
/user_profiles
/order_itemsПослідовність важливіша за конкретний варіант.
GET отримує представлення ресурсу або колекції.
GET /articles/42Властивості GET:
не повинен змінювати ресурс;
є безпечним методом;
може кешуватися;
може повторюватися без зміни результату на сервері.
Параметри фільтрації, сортування та пагінації зазвичай передаються в query string:
GET /articles?category=backend&sort=-publishedAt&page=2&limit=20POST зазвичай створює ресурс у колекції або запускає операцію, яка не має простої ідемпотентної моделі.
POST /articles
Content-Type: application/json
{
"title": "Принципи REST",
"content": "..."
}Якщо ресурс створено, сервер зазвичай повертає 201 Created і може вказати його адресу в заголовку Location:
HTTP/1.1 201 Created
Location: /articles/43POST не є ідемпотентним за замовчуванням. Повторна відправка може створити кілька ресурсів.
PUT замінює ресурс за вказаною адресою:
PUT /articles/42
Content-Type: application/json
{
"title": "Оновлений заголовок",
"content": "Новий текст"
}Повторення однакового PUT має давати той самий ефект, тому метод є ідемпотентним за правильної реалізації.
Важлива відмінність: PUT зазвичай означає повну заміну. Якщо клієнт надсилає лише одне поле, сервер може обнулити або видалити інші поля — залежно від контракту.
PATCH частково змінює ресурс:
PATCH /articles/42
Content-Type: application/json
{
"title": "Новий заголовок"
}PATCH краще використовувати, коли потрібно змінити лише частину даних. Його ідемпотентність залежить від конкретної операції.
Наприклад, встановлення значення:
{
"status": "published"
}можна повторювати без додаткового ефекту. А операція «збільшити лічильник на один» може давати інший результат при кожному повторенні.
DELETE видаляє ресурс:
DELETE /articles/42Успішна відповідь може мати статус:
204 No Content, якщо тіло відповіді не потрібне;
200 OK, якщо сервер повертає опис результату;
202 Accepted, якщо видалення виконуватиметься асинхронно.
Повторний DELETE часто реалізують ідемпотентно: після першого видалення ресурс уже відсутній. Проте другий запит може повернути 404 Not Found — це залежить від контракту API.
Статус відповіді має передавати семантику результату, а не бути завжди 200 OK.
Поширені статуси:
200 OK — успішне читання або оновлення;
201 Created — ресурс створено;
202 Accepted — запит прийнято для асинхронної обробки;
204 No Content — успішно, але без тіла відповіді;
400 Bad Request — некоректний запит;
401 Unauthorized — відсутня або недійсна автентифікація;
403 Forbidden — клієнт відомий, але не має дозволу;
404 Not Found — ресурс не знайдено;
409 Conflict — конфлікт із поточним станом ресурсу;
422 Unprocessable Content — структура запиту правильна, але дані не проходять бізнес-валідацію;
429 Too Many Requests — перевищено ліміт запитів;
500 Internal Server Error — внутрішня помилка сервера;
503 Service Unavailable — сервіс тимчасово недоступний.
401 і 403Ці статуси часто плутають:
401 означає, що клієнт не пройшов автентифікацію;
403 означає, що клієнт автентифікований, але не має потрібного дозволу.
Наприклад, користувач без входу отримує 401, а звичайний користувач, який намагається видалити чужий ресурс, — 403.
Один ресурс може мати різні представлення. Найчастіше API використовує JSON, але формат не є суттю REST.
Клієнт може вказати бажаний формат через Accept:
GET /articles/42 HTTP/1.1
Accept: application/jsonСервер повідомляє фактичний формат через Content-Type:
Content-Type: application/jsonНе обов’язково повертати клієнту всі внутрішні поля моделі. Представлення має бути частиною публічного контракту.
Наприклад, внутрішня модель користувача може містити хеш пароля, але API ніколи не має повертати його у відповіді.
Для одного ресурсу відповідь може виглядати так:
{
"id": 42,
"title": "Принципи REST",
"status": "published",
"publishedAt": "2026-07-29T10:00:00Z"
}Для колекції важливо передбачити пагінацію:
{
"items": [
{
"id": 42,
"title": "Принципи REST"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 135
}
}Назви полів і структура відповіді мають бути стабільними. Зміна типу поля з числа на рядок без зміни версії може зламати клієнтів.
Без пагінації великий список може:
перевантажити сервер;
збільшити час відповіді;
використати забагато пам’яті;
створити надмірне навантаження на мережу.
Поширені підходи:
сторінкова пагінація через page і limit;
курсорна пагінація через cursor;
обмеження кількості елементів із серверним максимумом.
Для часто змінюваних або дуже великих колекцій курсорна пагінація зазвичай надійніша, оскільки сторінки не так сильно зміщуються під час додавання нових записів.
Ці операції зазвичай описують query-параметрами:
GET /products?category=books&minPrice=10&maxPrice=50
GET /products?sort=-price,name
GET /products?q=restПотрібно визначити:
які поля можна фільтрувати;
які значення вважаються допустимими;
порядок сортування;
максимальний limit;
поведінку за невідомих параметрів.
Не слід безконтрольно передавати всі query-параметри без валідації: це може створити проблеми з безпекою та продуктивністю.
Формат помилок має бути передбачуваним. Наприклад:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Дані запиту не пройшли перевірку",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Некоректний формат електронної пошти"
}
],
"requestId": "req-8f31"
}
}Клієнт має орієнтуватися на машинний code, а не аналізувати текст повідомлення. Текст можна змінити або локалізувати, тоді як код повинен залишатися стабільним.
Не варто повертати клієнту:
stack trace;
SQL-запити;
внутрішні імена класів;
секрети;
службові дані інфраструктури.
REST не визначає конкретний спосіб автентифікації. API може використовувати:
сесійні cookie;
токени;
OAuth 2.0;
інші механізми залежно від системи.
Автентифікація відповідає на питання «хто це?», а авторизація — «що йому дозволено?».
Навіть правильно спроєктовані маршрути не захищають систему автоматично. Для кожної операції потрібно перевіряти:
чи автентифікований клієнт;
чи має він доступ до конкретного ресурсу;
чи може виконувати саме цю дію;
чи не обходить він обмеження через зміну ідентифікатора в URL.
Ідемпотентна операція може бути виконана кілька разів із тим самим кінцевим ефектом, що й один раз.
Зазвичай:
GET — ідемпотентний;
PUT — ідемпотентний;
DELETE — ідемпотентний за ефектом;
POST — неілемпотентний;
PATCH — залежить від операції.
Це важливо для мережевих помилок. Якщо клієнт не отримав відповідь, він може захотіти повторити запит. Повторити PUT зазвичай безпечніше, ніж POST.
Для створення ресурсів іноді використовують ключ ідемпотентності. Клієнт надсилає унікальний ідентифікатор операції, а сервер гарантує, що повтор із тим самим ключем не створить дубль. Такий механізм особливо корисний для платежів і замовлень.
ETagДва клієнти можуть одночасно змінювати один ресурс. Без захисту останній запис може непомітно перезаписати зміни першого клієнта.
Для оптимістичного контролю конкурентності використовують ETag і If-Match:
GET /articles/42 HTTP/1.1HTTP/1.1 200 OK
ETag: "article-42-v7"Під час оновлення клієнт передає отриману версію:
PUT /articles/42 HTTP/1.1
If-Match: "article-42-v7"
Content-Type: application/jsonЯкщо ресурс уже змінився, сервер може повернути 412 Precondition Failed. Це сигналізує клієнту, що потрібно повторно завантажити актуальні дані перед оновленням.
Зміни бувають сумісними та несумісними.
Сумісні зміни:
додавання необов’язкового поля;
додавання нового маршруту;
додавання нового значення за умови, що клієнти це підтримують.
Потенційно несумісні зміни:
видалення поля;
перейменування поля;
зміна типу;
зміна значення статусу;
зміна обов’язкового правила валідації;
зміна семантики наявного маршруту.
Для несумісних змін застосовують версіювання. Один із поширених варіантів:
/api/v1/articles
/api/v2/articlesВерсія в заголовку або через negotiation також можлива, але головне — мати чітку політику міграції та припинення підтримки старих версій.
Версіювання не повинно бути виправданням для неакуратного контракту. Краще спочатку проєктувати API так, щоб зміни можна було вносити поступово.
У повному REST-підході сервер може додавати до представлення посилання на доступні наступні дії. Це називають HATEOAS — Hypermedia as the Engine of Application State.
Наприклад:
{
"id": 42,
"status": "draft",
"links": {
"self": "/articles/42",
"publish": "/articles/42/publication",
"comments": "/articles/42/comments"
}
}Клієнт бачить не лише дані, а й можливі переходи. Це може зменшити жорстку прив’язаність клієнта до маршрутів.
На практиці багато API називають себе RESTful, хоча не використовують HATEOAS. Тому варто розрізняти:
API, яке використовує HTTP і ресурси;
API, яке дотримується всіх обмежень REST.
CRUD описує базові операції з даними:
Create;
Read;
Update;
Delete.
REST описує архітектуру взаємодії між клієнтом і сервером. CRUD часто добре відображається на REST-маршрути, але не охоплює:
кешування;
безстановість;
представлення;
єдиний інтерфейс;
гіпермедіа;
багатошарову архітектуру.
Не кожна бізнес-операція є простим CRUD. Наприклад, «підтвердити замовлення» змінює стан і може бути представлена як:
POST /orders/123/confirmationsабо як оновлення стану:
PATCH /orders/123
{
"status": "confirmed"
}Вибір залежить від доменної моделі. Якщо підтвердження є окремою подією з власними даними, окремий ресурс може бути кращим. Якщо це лише зміна стану, PATCH може бути достатнім.
POST для всіх операційPOST /getUsers
POST /updateUser
POST /deleteUserТак API втрачає семантику HTTP. Краще використовувати відповідний метод і ресурсний URL.
POST /articles/42/publishArticleІноді дія справді потребує окремого маршруту, але назва має бути короткою та узгодженою:
POST /articles/42/publicationабо ресурс може бути змінений через PATCH.
200 OKТакий підхід змушує клієнта аналізувати тіло відповіді, щоб зрозуміти, що сталося. HTTP-статус уже має для цього стандартизовану семантику.
Якщо один endpoint повертає:
{
"error": "Invalid email"
}а інший:
{
"message": "Validation failed",
"fields": {}
}клієнтам складно обробляти помилки. Формат має бути єдиним у всьому API.
PUT і PATCHКлієнт має розуміти:
чи PUT замінює всі поля;
чи допускається часткове тіло;
як обробляються пропущені поля;
чи PATCH підтримує конкретний формат змін.
Глибокі URL важко читати, документувати та підтримувати. Вкладеність має відображати реальну залежність ресурсів, а не структуру таблиць у базі даних.
Не слід автоматично серіалізувати модель бази даних у публічну відповідь. Внутрішня схема може змінитися, а клієнтський контракт має залишатися стабільним.
Потрібно обмежувати:
розмір тіла запиту;
кількість елементів у списку;
глибину фільтрів;
частоту запитів;
складність пошуку.
Інакше коректний із функціонального погляду endpoint може стати проблемою для продуктивності.
Перед реалізацією endpoint варто пройти кілька кроків.
Опишіть сутності домену:
users
articles
comments
ordersНе починайте з назв контролерів або SQL-таблиць.
З’ясуйте, які ресурси належать один одному, а які можуть існувати незалежно.
/articles/42/comments
/users/42/ordersДля кожної операції визначте:
метод;
URL;
тіло запиту;
можливі статуси;
формат успішної відповіді;
формат помилки;
правила авторизації.
Продумайте:
пагінацію;
сортування;
фільтрацію;
ліміти;
кешування;
ідемпотентність;
конкурентні оновлення.
API має передбачати:
відсутній ресурс;
некоректне тіло;
відсутню авторизацію;
недостатні права;
конфлікт станів;
перевищення ліміту;
тимчасову недоступність залежностей.
Контракт повинен бути зрозумілим не лише розробнику сервера. Його використовують клієнтські команди, тестувальники, DevOps-фахівці та інтегратори.
Документація має описувати не тільки успішний сценарій, а й помилки, обмеження та приклади запитів.
RESTful API — це не назви маршрутів і не формат JSON самі по собі. Його якість визначається тим, наскільки послідовно API використовує ресурси, HTTP-методи та стандартизовані правила взаємодії.
Основні принципи:
моделюйте іменовані ресурси, а не набір команд;
використовуйте HTTP-методи за їхньою семантикою;
робіть запити безстановими;
повертайте коректні HTTP-статуси;
визначайте стабільні представлення ресурсів;
проєктуйте єдиний формат помилок;
враховуйте кешування, пагінацію та конкурентні зміни;
захищайте ресурси перевіркою автентифікації й авторизації;
уникайте зайвої вкладеності та витоку внутрішньої моделі;
розглядайте API як довготривалий контракт, а не лише як набір маршрутів.