Gymterview
middle

Как проектировать REST API в Spring Boot?

Проектирование REST API в 2026 году строится по принципу API-first: сначала контракт (OpenAPI 3.1), потом реализация.

OpenAPI 3.1 спецификация

Пример OpenAPI-спецификации
openapi: 3.1.0
info:
  title: Order Service API
  version: 1.0.0

paths:
  /orders:
    post:
      operationId: createOrder
      summary: Создать новый заказ
      tags: [Orders]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        '201':
          description: Заказ создан
          headers:
            Location:
              schema:
                type: string
                format: uri
        '400':
          $ref: '#/components/responses/BadRequest'

  /orders/{orderId}:
    get:
      operationId: getOrder
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Успешный ответ
        '404':
          $ref: '#/components/responses/NotFound'

Контроллер с версионированием API

Версионирование через URL-путь (/api/v1/orders) — наиболее простой и распространённый подход:

Пример
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final CreateOrderUseCase createOrderUseCase;
    private final OrderDtoMapper mapper;

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(
            @Valid @RequestBody CreateOrderRequest request) {
        Order order = createOrderUseCase.create(mapper.toDomain(request));
        URI location = URI.create("/api/v1/orders/" + order.getId());
        return ResponseEntity.created(location).body(mapper.toResponse(order));
    }

    @GetMapping("/{orderId}")
    public OrderResponse getOrder(@PathVariable UUID orderId) {
        return mapper.toResponse(getOrderUseCase.getById(orderId));
    }
}

Problem Detail (RFC 9457) для ошибок

Spring Boot 3.x нативно поддерживает стандарт Problem Detail:

GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(OrderNotFoundException.class)
    public ProblemDetail handleOrderNotFound(OrderNotFoundException ex) {
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.NOT_FOUND, ex.getMessage());
        problem.setTitle("Заказ не найден");
        problem.setType(URI.create("https://api.example.com/errors/order-not-found"));
        problem.setProperty("orderId", ex.getOrderId());
        return problem;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.BAD_REQUEST, "Ошибка валидации");
        Map<String, String> errors = ex.getBindingResult().getFieldErrors().stream()
            .collect(Collectors.toMap(
                FieldError::getField,
                fe -> fe.getDefaultMessage() != null ? fe.getDefaultMessage() : "invalid",
                (a, b) -> a));
        problem.setProperty("fieldErrors", errors);
        return problem;
    }
}

REST vs gRPC vs GraphQL

Критерий REST gRPC GraphQL
Формат JSON Protobuf (бинарный) JSON
Контракт OpenAPI 3.1 .proto файлы Schema
Производительность Средняя Высокая Средняя
Подходит для Public API, CRUD Inter-service, высоконагруженные Гибкие клиентские запросы
Streaming Нет (SSE/WS) Да (native) Subscriptions

Частые ошибки

  • Возврат доменных сущностей напрямую из API вместо DTO — утечка внутренней структуры
  • Отсутствие версионирования API — любое breaking change ломает клиентов
  • Использование HTTP 200 для всех ответов с кодом ошибки в теле — нарушение семантики HTTP
  • Отсутствие пагинации для коллекций

На собеседовании: интервьюер часто спрашивает про обработку ошибок в API. Упомяните Problem Detail (RFC 9457) и покажите, что знаете разницу между 400 (валидация) и 422 (бизнес-ошибка). Вопрос “REST или gRPC?” — ответ зависит от контекста: REST для public API, gRPC для inter-service.