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

REST API: проєктування

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

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

Ресурси й методи HTTP, коди статусів, пагінація й фільтри, версіонування, ідемпотентність, контракт і документація - питання всіх рівнів, від junior до lead.

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

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

36 питань
  • 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 /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

Успіх (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

Клієнту API потрібно не лише знати, що пішло не так, а й обробити це програмно. Для цього помилки мають бути в одному передбачуваному форматі на весь API.

Problem Details (RFC 9457, замінив RFC 7807) - стандартний формат з типом application/problem+json:

{
  "type": "https://api.example.com/problems/insufficient-funds",
  "title": "Недостатньо коштів",
  "status": 422,
  "detail": "На рахунку 30 грн, а списання - 50 грн.",
  "instance": "/payments/8f3a",
  "balance": 3000
}
  • type - URI типу помилки, стабільний ідентифікатор для програмної обробки (бажано - з документацією за цією адресою).
  • title - короткий опис типу, однаковий для всіх його випадків.
  • status - HTTP-код (дублює заголовок для зручності).
  • detail - пояснення саме цього випадку.
  • instance - ідентифікатор конкретного випадку.
  • Можна додавати власні поля - як balance вище чи список помилок валідації за полями.

Принципи, незалежно від формату:

  • Правильний HTTP-код - формат тіла його не замінює.
  • Машиночитний код помилки (type чи code), а не лише текст: текст змінюють і перекладають, а клієнти на нього не мають покладатися.
  • Помилки валідації за полями, щоб клієнт підсвітив поля форми.
  • Жодних стек-трейсів, SQL і шляхів до файлів у відповіді на проді.
  • ID запиту в тілі чи заголовку - щоб знайти помилку в логах за зверненням клієнта.

Докладніше в документації: RFC 9457: Problem Details for HTTP APIs

Основні правила:

  • Іменники, а не дієслова: дія задається HTTP-методом. GET /orders, POST /orders, DELETE /orders/42 - а не /getOrders, /createOrder.
  • Множина для колекцій: /orders і /orders/42 - узгоджено для всього API.
  • Вкладеність для відношень, але неглибока: /users/42/orders - так; /users/42/orders/7/items/3/reviews - ні. Ресурс з власним ID краще адресувати напряму: /order-items/3.
  • Нижній регістр і дефіси: /payment-methods, а не /paymentMethods чи /payment_methods.
  • Фільтрація, сортування, пагінація - у query-параметрах: /orders?status=paid&sort=-created_at&page[size]=20.
  • Стабільні ідентифікатори в URL: ID чи UUID, а не назви, які можуть змінитися.

Дії, що не вкладаються в CRUD:

  • як під-ресурс стану: POST /orders/42/cancellation;
  • як дія-під-ресурс: POST /orders/42/cancel - прагматично й зрозуміло, і так роблять великі API (Stripe, GitHub);
  • головне - узгодженість у межах API.

Відповіді:

  • Узгоджене іменування полів (snake_case чи camelCase - одне на весь API).
  • Дати - в ISO 8601 з часовим поясом.
  • Гроші - цілими числами в мінімальних одиницях або рядками, з валютою.
  • Посилання на пов'язані ресурси чи їхні ID, а не дублювання цілих об'єктів без потреби.

Найважливіше - передбачуваність: розробник, побачивши два ендпоінти, має вгадати третій. Опис в OpenAPI допомагає тримати цю узгодженість і генерувати клієнти.

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

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

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