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

Junior: питання на співбесіді з теми «Проєктування API»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

5 питань

  • 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

Успіх (2xx):

  • 200 OK - звичайна успішна відповідь з тілом.
  • 201 Created - ресурс створено; бажано з заголовком Location.
  • 202 Accepted - запит прийнято, обробка асинхронна (поставлено в чергу).
  • 204 No Content - успіх без тіла: видалення, оновлення без повернення даних.

Помилки клієнта (4xx):

  • 400 Bad Request - запит некоректний: зламаний JSON, неправильний формат.
  • 401 Unauthorized - не автентифіковано: немає токена чи він недійсний. Попри назву, це про «хто ви?».
  • 403 Forbidden - автентифіковано, але немає прав на цю дію.
  • 404 Not Found - ресурсу немає (або ви не маєте права знати, що він існує).
  • 409 Conflict - конфлікт стану: дублікат, застаріла версія при оптимістичному блокуванні.
  • 422 Unprocessable Content - синтаксис правильний, але дані не пройшли валідацію. Laravel так і відповідає на помилки валідації.
  • 429 Too Many Requests - перевищено ліміт запитів, з Retry-After.

Помилки сервера (5xx): 500 - непередбачена помилка; 502/503/504 - проблеми з upstream, перевантаження, обслуговування.

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

  • 200 OK з {"success": false, "error": "..."} - клієнти, проксі й моніторинг вважатимуть запит успішним.
  • 500 на помилку валідації - це не помилка сервера.
  • 403 для чужого ресурсу інколи розкриває, що він існує; тоді свідомо віддають 404.

Докладніше в документації: Коди стану HTTP

REST (Representational State Transfer) - архітектурний стиль, описаний Роєм Філдінгом. Це не протокол і не формат, а набір обмежень:

  • ресурси й ідентифікатори: усе, з чим працює API, - ресурси з власними URL (/orders/42), а не «процедури»;
  • уніфікований інтерфейс: дії виражаються стандартними HTTP-методами (GET, POST, PUT, PATCH, DELETE) з їхньою семантикою;
  • представлення: клієнт отримує не сам ресурс, а його представлення (JSON, XML) - залежно від заголовка Accept;
  • без стану (stateless): кожен запит містить усе потрібне для обробки (зокрема автентифікацію). Сервер не пам'ятає «сесію розмови» між запитами;
  • кешованість: відповіді позначають, чи можна їх кешувати;
  • багаторівнева система: між клієнтом і сервером можуть бути проксі, CDN, балансувальники - і клієнт про це не знає.

«Просто JSON через HTTP» (стиль RPC) виглядає інакше:

POST /api/getOrder         {"id": 42}
POST /api/cancelOrder      {"id": 42}
POST /api/updateOrderStatus

RESTful:

GET    /api/orders/42
PATCH  /api/orders/42      {"status": "cancelled"}
DELETE /api/orders/42

Що дає REST на практиці:

  • передбачуваність: знаючи URL ресурсу й методи HTTP, легко здогадатися, як працювати з API;
  • інфраструктура HTTP працює «з коробки»: GET кешується проксі й браузерами, безпечні методи можна повторювати, коди стану зрозумілі моніторингу;
  • масштабування: відсутність стану на сервері дає змогу додавати сервери за балансувальником без «липких» сесій.

Реальність: більшість «REST API» не виконують усіх обмежень (зокрема гіпермедіа - HATEOAS), і це нормально. На співбесіді важливо розуміти суть - ресурси, семантика методів і кодів, відсутність стану, - а не догматичність.

RPC-стиль не заборонений: для дій, що не вкладаються в CRUD, чи для внутрішніх сервісів RPC (зокрема gRPC) інколи природніший. Головне - послідовність у межах одного API.

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

Безпечний метод не змінює стан на сервері - лише читає. Ідемпотентний метод - повторний виклик з тими самими даними дає той самий стан сервера, що й одиночний.

Метод Безпечний Ідемпотентний
GET, HEAD, OPTIONS так так
PUT ні так
DELETE ні так
POST ні ні
PATCH ні не гарантовано

Приклади:

  • PUT /users/7 {"name": "Оля"} - один раз чи п'ять, результат той самий: ім'я «Оля»;
  • DELETE /orders/42 - перший виклик видаляє, наступні нічого не змінюють (відповідь може бути 404, але стан однаковий);
  • POST /orders - кожен виклик створює нове замовлення;
  • PATCH {"balance": {"increment": 100}} - не ідемпотентний, а PATCH {"status": "paid"} - фактично ідемпотентний. Тому про PATCH кажуть «не гарантовано».

Ідемпотентність стосується стану, а не відповіді. Відповіді можуть відрізнятися (200 і потім 404 для DELETE), і updated_at може змінитися - важливо, що ефект для клієнта той самий.

Чому це важливо:

  • повтори при збоях мережі. Клієнт не отримав відповіді - він не знає, чи запит виконався. Ідемпотентний запит можна безпечно повторити. Неідемпотентний - ні: повтор POST /payments може списати гроші двічі. Для таких операцій потрібен ключ ідемпотентності;
  • проксі, браузери, бібліотеки автоматично повторюють і попередньо завантажують безпечні запити. Якщо GET /logout чи GET /orders/42/delete змінює стан, прийде «невидимий» користувач - пошуковий робот, попереднє завантаження посилань - і виконає дію;
  • кешування: відповіді на безпечні методи можна кешувати.

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

  • зміна стану в GET (лічильники, «відмітити прочитаним», видалення за посиланням);
  • PUT, реалізований як «додати до наявного» (тоді він не ідемпотентний);
  • POST для читання з великим тілом запиту - допустимо як виняток (складний пошук), але відповідь не кешується, і повтор не очевидно безпечний.

У Laravel CSRF-захист і так не перевіряє GET/HEAD/OPTIONS - ще одна причина не змінювати стан у них.

Докладніше в документації: Ідемпотентність

JSON як формат прийнятний будь-який, але непослідовність у межах API змушує клієнтів писати виняток на кожен ендпойнт. Домовленості варто прийняти заздалегідь і дотримуватися всюди.

1. Стиль ключів - один на весь API: snake_case (природний для Laravel і баз даних) або camelCase (природний для JavaScript). Головне - не змішувати: created_at в одній відповіді й updatedAt в іншій.

2. Дати й час - ISO 8601 / RFC 3339 з часовим поясом:

{ "created_at": "2026-10-04T10:15:00+03:00", "paid_at": "2026-10-04T07:15:00Z" }

Не "04.10.2026", не мітка часу без пояснення, не локальний час без зміщення. «Дата без часу» (день народження) - окремий формат "2026-10-04".

3. Гроші - не числа з рухомою комою: мінімальні одиниці цілим числом ("amount": 125050 копійок) або рядок ("125.50") плюс явна валюта.

4. Ідентифікатори - рядки, якщо можуть бути великими: числа понад 2^53 JavaScript округлює. Для BIGINT чи Snowflake-ID безпечніше "id": "9007199254740993".

5. null чи відсутнє поле: вирішити й задокументувати. Поширений підхід - поле завжди є, null означає «немає значення». Відсутність поля - лише для свідомо необов'язкових чи прихованих правами.

6. Обгортка відповіді: { "data": ..., "meta": ..., "links": ... } для колекцій (пагінація в meta) - так роблять ресурси Laravel. Обгортка дає місце для метаданих без зміни структури даних.

7. Енуми - рядки, а не числа: "status": "paid" читається й не ламається при зміні порядку значень.

8. Булеві значення - true/false, а не 1/0 чи "yes".

9. Помилки - єдиний формат для всіх ендпойнтів (наприклад, Problem Details).

10. Без «магічних» значень: -1 замість null, порожній рядок замість відсутності.

Технічні деталі JSON:

  • кодування - UTF-8 (вимога RFC 8259 для обміну між системами);
  • порядок ключів в об'єкті не гарантується - клієнт не повинен на нього покладатися;
  • дублікати ключів - невизначена поведінка, різні парсери обирають різне значення.

У Laravel: касти дат у моделях ('paid_at' => 'datetime') серіалізуються в ISO 8601 UTC, API Resources керують ключами й форматом явно.

Докладніше в документації: RFC 8259: формат JSON