Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

REST API: Загальний

20 питань · ~18 хв · Версія v3.0

Увійдіть, щоб продовжити

HTTP-методи й статус-коди, дизайн ресурсів, пагінація, кешування, ідемпотентність, автентифікація, помилки й API у Laravel.

За спробу
20
У пулі
100
Проходжень
0
Середній бал
-
Пройшли на 70%+
-

Питання для підготовки

100 питань
  • POST - створити ресурс (або виконати дію). Сервер сам обирає адресу нового ресурсу: POST /orders → 201 Created з Location: /orders/42.
  • PUT - замінити ресурс цілком тим, що в тілі запиту. Поля, яких немає в тілі, вважаються видаленими чи скинутими.
  • PATCH - частково змінити ресурс: лише передані поля.
PUT /users/42
{"name": "Оля", "email": "olia@example.com", "phone": null}

PATCH /users/42
{"phone": "+380501234567"}

Ідемпотентність - ключова відмінність, про яку питають:

  • PUT і DELETE ідемпотентні: повторний однаковий запит лишає ресурс у тому самому стані. Їх можна безпечно повторити при обриві з'єднання.
  • POST не ідемпотентний: повтор створить друге замовлення.
  • PATCH - залежить від змісту: «встановити phone» ідемпотентний, «збільшити лічильник на 1» - ні.
  • GET, HEAD, OPTIONS - ще й безпечні: не змінюють стан узагалі.

На практиці:

  • Багато API використовують лише PATCH для оновлень, бо клієнту рідко потрібно надсилати весь ресурс.
  • Для створення з повторами (платежі) POST роблять ідемпотентним через заголовок Idempotency-Key.
  • Дії, що не вкладаються в CRUD (/orders/42/cancel), зазвичай оформлюють як POST на під-ресурс.

Докладніше в документації: HTTP-метод PUT

Проблема: клієнт відправив POST /payments, а відповідь не дійшла - таймаут, обрив мережі. Чи пройшов платіж? Повторити запит небезпечно (подвійне списання), не повторити - теж (платіж може не пройти).

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

POST /payments
Idempotency-Key: 4f0f9c2e-8b1a-4c3e-9d5f-2a7b6c8d9e01
{"amount": 5000, "currency": "UAH"}

Реалізація на сервері:

  1. Отримати ключ і атомарно зарезервувати його (унікальний індекс у таблиці чи SET NX у Redis) разом з ID користувача.
  2. Якщо ключ новий - виконати операцію й зберегти статус і тіло відповіді.
  3. Якщо ключ уже є й операція завершена - повернути збережену відповідь.
  4. Якщо ключ є, але операція ще виконується (паралельний повтор) - 409 Conflict, щоб клієнт повторив пізніше.
  5. Якщо той самий ключ прийшов з іншим тілом запиту - 422: це помилка клієнта, ключ використано повторно для іншої операції.

Деталі, про які питають:

  • Ключ прив'язують до користувача, щоб чужий ключ не дав доступ до чужої відповіді.
  • Термін зберігання ключів - зазвичай 24 години.
  • Зберігати відповідь потрібно в тій самій транзакції, що й результат операції, інакше падіння між ними знову дасть подвійне виконання.
  • Помилки валідації (4xx) зазвичай зберігають, а тимчасові збої (5xx) - ні, щоб повтор мав шанс пройти.

Саме так працюють Stripe і більшість платіжних API. IETF стандартизує заголовок Idempotency-Key в окремій специфікації.

Докладніше в документації: Чернетка IETF: Idempotency-Key

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

Сумісні зміни (можна будь-коли):

  • додати новий ендпоінт;
  • додати необов'язкове поле в запит;
  • додати поле у відповідь (клієнти мають ігнорувати невідомі поля - це варто прописати в документації);
  • додати нове значення в перелік - обережно: клієнт зі строгою перевіркою enum може зламатися.

Несумісні (ламаючі): видалити чи перейменувати поле, змінити тип чи формат, зробити поле обов'язковим, змінити зміст коду відповіді, змінити поведінку за замовчуванням.

Версіонування для ламаючих змін:

  • в URL - /v1/orders, /v2/orders: найпростіше й найпомітніше;
  • в заголовку - Accept: application/vnd.example.v2+json або власний заголовок;
  • датою - як Stripe (Stripe-Version: 2025-03-31): кожен клієнт закріплений на версії API на момент інтеграції, а сервер перетворює відповіді для старих версій.

Плавне виведення старого:

  1. Оголосити застарілість у документації й журналі змін.
  2. Додати заголовки: Deprecation (RFC 9745) - що ресурс застарів, Sunset (RFC 8594) - дата, після якої він перестане працювати, і Link на документацію з міграцією.
  3. Моніторити використання старої версії за клієнтами й писати тим, хто ще на ній.
  4. Вимикати лише після дати й коли трафік зійшов нанівець.

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

Докладніше в документації: RFC 9745: заголовок Deprecation

Коли ваш сервіс викликає інший (мікросервіс, партнерський API, внутрішній воркер), потрібно довести, який сервіс робить запит, і що запит не змінено по дорозі.

1. Статичний API-ключ / спільний секрет у заголовку - найпростіше:

Authorization: Bearer svc_billing_8f2a...

Мінуси: довгоживучий секрет, що зберігається в кількох місцях; викрадений ключ працює звідусіль; ротація болісна. Прийнятно для простих інтеграцій із ротацією й обмеженням за IP.

2. Підпис запиту (HMAC) - секрет не передається мережею, передається підпис:

X-Timestamp: 1760000000
X-Signature: hex(HMAC-SHA256(secret, method + path + timestamp + sha256(body)))

Отримувач обчислює підпис сам і порівнює у постійному часі (hash_equals). Мітка часу з коротким вікном (кілька хвилин) захищає від повторного відтворення. Так підписують вебхуки (Stripe, GitHub) і запити AWS (SigV4).

3. Взаємний TLS (mTLS) - обидві сторони пред'являють сертифікати:

  • сервер перевіряє клієнтський сертифікат, виданий вашим внутрішнім центром сертифікації;
  • ідентичність сервісу - у сертифікаті, а не в секреті в коді;
  • RFC 8705 описує прив'язку OAuth-токенів до сертифіката: викрадений токен без приватного ключа клієнта непридатний.

Мінус - інфраструктура: власний CA, видача й ротація сертифікатів. Service mesh (Istio, Linkerd) роблять mTLS прозорим для застосунку.

4. Короткоживучі токени від центрального сервісу ідентифікації - OAuth 2.0 Client Credentials:

POST /oauth/token
grant_type=client_credentials&client_id=billing&client_secret=...&scope=orders:read

Сервіс отримує токен на хвилини з конкретними правами (scopes), отримувач перевіряє підпис і aud. У хмарах - ідентичності робочих навантажень (IAM-ролі, workload identity) без статичних секретів узагалі.

Як обрати:

  • дві-три внутрішні інтеграції - підписані запити з ротацією секретів;
  • багато сервісів, потрібні права й аудит - OAuth client credentials (у Laravel - Passport);
  • мережа з нульовою довірою, високі вимоги - mTLS, часто разом із токенами.

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

Докладніше в документації: RFC 8705: OAuth 2.0 з mTLS

Запит складається з рядка запиту, заголовків і (необов'язково) тіла:

POST /api/orders HTTP/1.1
Host: shop.example.com
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
Idempotency-Key: 8f14e45f-ceea-467f-a0d6-2a1e6c0c4f11

{"product_id": 42, "qty": 2}
  • метод (GET, POST, PATCH...) - що зробити;
  • шлях і рядок запиту - з яким ресурсом;
  • заголовки - метадані: формат, автентифікація, кешування;
  • тіло - дані (для GET тіла зазвичай немає).

Відповідь - рядок статусу, заголовки, тіло:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/orders/1057
Cache-Control: no-store

{"data": {"id": 1057, "status": "new"}}

Заголовки, які варто знати для API:

Заголовок Навіщо
Content-Type формат тіла, яке надсилається
Accept формат, який клієнт хоче отримати
Authorization облікові дані (Bearer-токен)
Location адреса створеного ресурсу (з 201) чи статусу операції (з 202)
Cache-Control, ETag кешування й умовні запити
Retry-After коли повторити (з 429, 503)
X-Request-Id / traceparent наскрізний ідентифікатор для логів і трасування

Типові помилки:

  • Content-Type не відповідає тілу - сервер не розбере JSON, надісланий як text/plain;
  • відсутній Accept: application/json у запитах до Laravel - при помилці валідації замість JSON 422 прийде редирект на попередню сторінку;
  • статус 200 з {"error": ...} у тілі - клієнти, проксі й моніторинг орієнтуються на код статусу, тож помилка має бути помилкою на рівні HTTP;
  • власні заголовки з префіксом X- - застарілий звичай (RFC 6648); нові заголовки називають без нього.

Налагодження: curl -i показує заголовки відповіді, curl -v - і запиту; у браузері - вкладка Network.

Докладніше в документації: Огляд HTTP

Прочитати - ще не значить знати

20 питань, по одному на екран, ~18 хв. Після завершення - розбір кожної помилки з посиланням на питання.