Що таке RESTful API дизайн
В основі RESTful дизайну лежить ресурсно-орієнтована архітектура (Resource-Oriented Architecture, ROA):
🟢 Junior Level
RESTful API дизайн — це підхід до проектування програмних інтерфейсів, що базується на архітектурних принципах REST (Representational State Transfer) та семантиці протоколу HTTP.
В основі RESTful дизайну лежить ресурсно-орієнтована архітектура (Resource-Oriented Architecture, ROA):
- Ресурси (Resources) — це сутності предметної області (користувачі, замовлення, товари). Ресурси завжди ідентифікуються через URI з використанням іменників у множині (
/users,/orders). - Дії (Actions) — виражаються стандартними HTTP-методами (
GET,POST,PUT,PATCH,DELETE). - Результат операції — виражається стандартними HTTP статус-кодами (
200,201,204,400,404,500).
Канонічний приклад RESTful ендпоінтів
| HTTP Метод | URI | Семантичне призначення | Успішний HTTP статус |
|---|---|---|---|
GET |
/api/v1/users |
Отримати список користувачів (з пагінацією) | 200 OK |
GET |
/api/v1/users/42 |
Отримати конкретного користувача з ID 42 | 200 OK |
POST |
/api/v1/users |
Створити нового користувача | 201 Created (+ Location) |
PUT |
/api/v1/users/42 |
Повністю замінити дані користувача 42 | 200 OK або 204 No Content |
PATCH |
/api/v1/users/42 |
Частково оновити поля користувача 42 | 200 OK |
DELETE |
/api/v1/users/42 |
Видалити користувача 42 | 204 No Content |
Головні антипатерни в іменуванні
- ❌
GET /api/getUsers— дієслово в URI дублює HTTP-методGET. - ❌
POST /api/deleteUser?id=42— методPOSTвикористовується замість ідемпотентногоDELETE. - ❌
POST /api/user/save— RPC-стиль у дусі застарілих SOAP-сервісів.
🟡 Middle Level
Модель зрілості Річардсона (Richardson Maturity Model)
Леонард Річардсон виділив 4 рівні відповідності вебсервісу істинному REST:
┌────────────────────────────────────────────────────────┐
│ Level 3: HATEOAS (Hypermedia as the Engine of State) │
├────────────────────────────────────────────────────────┤
│ Level 2: HTTP Verbs & Status Codes (Стандарт галузі) │
├────────────────────────────────────────────────────────┤
│ Level 1: Resources & Individual URIs (/users/42) │
├────────────────────────────────────────────────────────┤
│ Level 0: The Swamp of POX (RPC over HTTP POST) │
└────────────────────────────────────────────────────────┘
- Level 0 (The Swamp of POX): HTTP використовується як сліпий транспортний тунель. Усі запити надсилаються на один URL (
/api/service) методомPOST(SOAP, XML-RPC, JSON-RPC). - Level 1 (Resources): Поява окремих URI для кожної сутності (
/users/1,/orders/2), проте взаємодія відбувається через один або два методи (наприклад, усі дії —POST). - Level 2 (HTTP Verbs & Status Codes): Золотий стандарт комерційної розробки. Застосовуються правильні HTTP методи (
GET,POST,PUT,DELETE), статуси відповідей (201,204,404) та заголовки. - Level 3 (HATEOAS): Сервер разом із даними повертає посилання на можливі подальші дії клієнта. У 95% промислових систем вважається надлишковим через високий оверхед на парсинг та передачу посилань.
Фільтрація, пагінація та сортування
Для вибірки підмножин ресурсів використовуються Query Parameters, а не створення нових URI:
- Пагінація (Offset / Limit):
GET /api/v1/orders?offset=20&limit=10 - Курсорна пагінація (Cursor-based):
GET /api/v1/orders?cursor=eyJpZCI6MTAxfQ&limit=20(критично для динамічних стрічок і великих таблиць, виключає зсув записів під час вставок). - Сортування:
GET /api/v1/products?sort=price,desc&sort=name,asc - Фільтрація:
GET /api/v1/tickets?status=OPEN&priority=HIGH
Вирішення проблеми N+1 у REST (Sparse Fieldsets та Embedding)
На відміну від GraphQL, REST API часто страждає від надлишкової вибірки (Over-fetching) або проблеми N+1 HTTP-запитів. У RESTful дизайні це вирішується двома патернами:
- Sparse Fieldsets: вибірка лише необхідних клієнту полів:
GET /api/v1/users/42?fields=id,email,displayName - Resource Embedding: впровадження пов’язаних сутностей:
GET /api/v1/orders/101?embed=customer,items
🔴 Senior Level
Design-First (API-First) vs Code-First
В ентерпрайз-розробці вибір підходу визначає швидкість та якість інтеграції:
| Критерій | API-First (Design-First) | Code-First |
|---|---|---|
| Методологія | Спочатку створюється OpenAPI 3.1 специфікація (YAML), потім генерується код. | Спочатку пишуться Spring контролери, документація генерується SpringDoc. |
| Паралельна робота | Фронтенд, мобільні клієнти та бекенд розробляються одночасно за Mock-сервером (Prism). | Клієнтські команди змушені чекати на готовність бекенду. |
| Відповідність контракту | 100% гарантія відповідності документації та API. Лінтування через Spectral. | Документація може розходитися з реальною поведінкою під час складних мапінгів. |
| Інструменти | openapi-generator-maven-plugin, Stoplight, SwaggerHub. |
springdoc-openapi-starter-webmvc-ui. |
Архітектура Spring MVC: обробка REST запитів
За лаштунками Spring Boot за обробку запиту відповідають:
DispatcherServletприймає вхідний HTTP-запит.RequestMappingHandlerMappingза таблицею маршрутів знаходить відповідний метод класу@RestController.HandlerAdapterготує виклик методу, активуючиHandlerMethodArgumentResolver(парсинг@RequestBody,@PathVariable,@RequestParam).HttpMessageConverter(JacksonMappingJackson2HttpMessageConverter) десеріалізує тіло запиту з JSON і серіалізує відповідь у JSON.
Зразковий Spring Boot 3 RESTful контролер
package com.example.controller;
import com.example.dto.CreateUserRequest;
import com.example.dto.UpdateUserRequest;
import com.example.dto.UserResponseDto;
import com.example.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 1. Отримання колекції з пагінацією та сортуванням: 200 OK
@GetMapping
public ResponseEntity<Page<UserResponseDto>> getUsers(
@RequestParam(required = false) String status,
@PageableDefault(size = 20, sort = "createdAt") Pageable pageable) {
return ResponseEntity.ok(userService.findAll(status, pageable));
}
// 2. Отримання одного ресурсу: 200 OK
@GetMapping("/{id}")
public ResponseEntity<UserResponseDto> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
// 3. Створення ресурсу: 201 Created + заголовок Location
@PostMapping
public ResponseEntity<UserResponseDto> createUser(@Valid @RequestBody CreateUserRequest request) {
UserResponseDto created = userService.create(request);
// Формуємо URI створеного ресурсу: /api/v1/users/{id}
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(created.id())
.toUri();
return ResponseEntity.created(location).body(created);
}
// 4. Повна заміна: 200 OK
@PutMapping("/{id}")
public ResponseEntity<UserResponseDto> replaceUser(@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
return ResponseEntity.ok(userService.update(id, request));
}
// 5. Видалення ресурсу: 204 No Content
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
🎯 Шпаргалка для інтерв’ю
30-секундна відповідь
RESTful API дизайн — це архітектурний підхід до проєктування вебсервісів, за якого API структурується навколо ресурсів (іменники у множині), дії над ними виконуються стандартними HTTP-методами (
GET,POST,PUT,PATCH,DELETE), а результат повертається стандартними HTTP-статус-кодами.Згідно з моделлю Річардсона (Richardson Maturity Model), сучасним промисловим стандартом є Level 2 (Resources + HTTP Verbs + Status Codes). Фільтрація та пагінація виносяться в query-параметри. В enterprise-проєктах стандартом є підхід API-First (Design-First) з описом контракту в OpenAPI 3.1 до написання коду.
4 каверзних питання з відповідями
-
Що таке Richardson Maturity Model і чому Level 3 (HATEOAS) рідко використовується на практиці? Відповідь: Модель зрілості описує еволюцію від чистого RPC (Level 0) через ресурси (Level 1) та HTTP-методи зі статусами (Level 2) до гіпермедіа HATEOAS (Level 3). HATEOAS рідко застосовується через високий оверхед на формування графа посилань, роздування розміру JSON на 30–50% та складність його підтримки на мобільних клієнтах, яким простіше працювати з фіксованими контрактами OpenAPI.
-
Навіщо під час створення ресурсу методом POST повертати заголовок Location? Відповідь: Заголовок
Location: /api/v1/users/42вказує клієнту точний канонічний URI новоствореного підпорядкованого ресурсу відповідно до специфікації RFC 9110, дозволяючи клієнту одразу перейти до читання об’єкта без ручної конкатенації рядків. -
У чому різниця між підходом Design-First та Code-First під час проєктування REST API? Відповідь: За Design-First спочатку створюється OpenAPI-контракт, узгоджуються схеми даних між бекендом, фронтендом і мобільними розробниками, і лише після цього генерується код. За Code-First розробники спочатку пишуть контролери, а документація генерується постфактум. Design-First мінімізує інтеграційні ризики та дозволяє вести паралельну розробку.
-
Як у RESTful дизайні правильно відобразити нестандартну дію, наприклад, «заблокувати користувача» або «переказати кошти»? Відповідь: Через підпорядковані ресурси дій:
POST /users/42/block-actionsабо зміну стану через PATCH:PATCH /users/42 {"status": "BLOCKED"}. Для фінансових операцій створюється ресурс транзакції:POST /transfersз тілом запиту, а не виклик RPC-дієслова/users/42/transferMoney.
Червоні прапорці (чого категорично не можна говорити)
- ❌ «У REST API дієслова в URL — це ознака гарного тону для зрозумілості» (дієслова в URI порушують принципи REST; дія зобов’язана виражатися HTTP-методом).
- ❌ «Для всіх операцій потрібно повертати 200 OK, а статус помилки передавати в полі JSON status» (ламає стандарт HTTP, позбавляє проміжні кеші та моніторинг можливості відстежувати збої).
- ❌ «REST API завжди вимагає використання HATEOAS, інакше це не REST» (у прагматичній індустрії переважна більшість сервісів працюють на Richardson Level 2).
- ❌ «Query параметри та Path variables абсолютно взаємозамінні» (Path змінні ідентифікують ресурс, а Query параметри фільтрують, сортують і налаштовують пагінацію вибірки).