REST API Style Guide: JSON и формат ответов

Именование полей в JSON. Формат ответов REST API: ресурсы, коллекции, null-поля.

REST API JSON формат ответов

REST API JSON формат ответов: 10. Именование полей в JSON

10.1. camelCase

REST API JSON формат ответов — {
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "createdAt": "2025-03-15T10:30:00Z",
  "totalAmount": 1500.00,
  "deliveryAddress": {
    "streetName": "Ленина",
    "zipCode": "123456"
  }
}

10.2. Даты -- ISO 8601

{
  "createdAt": "2025-03-15T10:30:00Z",
  "dateFrom": "2025-01-01",
  "dateTo": "2025-12-31"
}

10.3. Enum-значения -- UPPER_SNAKE_CASE

{
  "status": "IN_PROGRESS",
  "paymentMethod": "CREDIT_CARD"
}

10.4. Коллекции -- множественное число

{
  "items": [{ "itemId": "..." }, { "itemId": "..." }],
  "errors": [{ "code": "...", "message": "..." }],
  "tags": ["electronics", "sale"]
}

10.5. Boolean -- префикс is / has / can (опционально)

{
  "active": true,
  "hasDiscount": false,
  "canCancel": true
}

Допустимо и без префикса ("active": true), главное -- единообразие в рамках проекта.

10.6. ID -- суффикс Id

{
  "orderId": "...",
  "customerId": "...",
  "parentCategoryId": "..."
}

11. Формат ответов

11.1. Единичный ресурс

Ответ на GET /api/v1/orders/{id} -- плоский объект ресурса без обёртки:

{
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "CONFIRMED",
  "totalAmount": 1500.00,
  "createdAt": "2025-03-15T10:30:00Z",
  "items": [
    {
      "itemId": "...",
      "productName": "Клавиатура",
      "quantity": 2,
      "price": 750.00
    }
  ]
}
  • Никакой обёртки вида { "data": { ... } } -- ресурс возвращается как корневой объект
  • Вложенные объекты и коллекции допустимы внутри ресурса

11.2. Коллекция (список)

Ответ на GET /api/v1/orders -- объект с массивом content и метаданными пагинации. Формат зависит от типа пагинации (см. раздел 9.4).

  • Поле с данными всегда называется content -- это не envelope-обёртка, а стандартная структура пагинированного ответа
  • Метаданные пагинации -- на том же уровне, что и content, не вложенные
  • Пустой список -- "content": [], не null, не отсутствие поля

11.3. Создание ресурса (POST)

Код ответа 201 Created. Тело -- созданный ресурс целиком. Заголовок Location -- URL нового ресурса:

HTTP/1.1 201 Created
Location: /api/v1/orders/550e8400-e29b-41d4-a716-446655440000

{
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "CREATED",
  "totalAmount": 0,
  "createdAt": "2025-03-15T10:30:00Z"
}

11.4. Обновление ресурса (PUT / PATCH)

Код ответа 200 OK. Тело -- обновленный ресурс целиком:

HTTP/1.1 200 OK

{
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "CONFIRMED",
  "totalAmount": 1500.00,
  "updatedAt": "2025-03-15T11:00:00Z"
}

11.5. Удаление ресурса (DELETE)

Код ответа 204 No Content. Тело пустое:

HTTP/1.1 204 No Content

11.6. Action-эндпоинты

Код ответа 200 OK. Тело -- обновленный ресурс:

POST /api/v1/orders/{id}/confirm

HTTP/1.1 200 OK

{
  "orderId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "CONFIRMED",
  "confirmedAt": "2025-03-15T11:00:00Z"
}

Если действие асинхронное -- 202 Accepted. Подробности о паттерне polling см. в разделе 18.

11.7. Null и отсутствующие поля

  • Не включать поля со значением null -- если поле не заполнено, его не должно быть в ответе. Это уменьшает размер ответа и упрощает контракт
  • Пустые коллекции -- [], не null -- если у заказа нет позиций, возвращать "items": []
  • Пустые строки -- не использовать "" вместо отсутствия поля. Если значения нет -- не включать поле
{
  "orderId": "...",
  "status": "CREATED",
  "items": [],
  "comment": "Доставить до 18:00"
}

Поле discount отсутствует (а не "discount": null), items пустой массив (а не null).

Как отразить в OpenAPI: поля, которые могут отсутствовать в ответе, не включаются в required. Поля, которые присутствуют всегда, указываются в required. Не использовать nullable: true -- поле либо есть, либо отсутствует:

OrderResponse:
  type: object
  required:
    - orderId
    - status
    - items
  properties:
    orderId:
      type: string
      format: uuid
    status:
      $ref: '#/components/schemas/OrderStatus'
    items:
      type: array
      items:
        $ref: '#/components/schemas/OrderItemResponse'
    discount:
      type: number
      description: 'Отсутствует если скидки нет'
    comment:
      type: string
      description: 'Отсутствует если не заполнен'

11.8. Обёртки (envelope)

Обёртки вида { "data": ..., "meta": ..., "success": true } НЕ используем для единичных ресурсов:

  • HTTP-статус уже сообщает об успехе/ошибке
  • Ошибки возвращаются в формате RFC 9457 (раздел 13)
  • Коллекции оборачиваются в { "content": [...] } -- это не envelope, а структура пагинированного ответа с метаданными (см. раздел 11.2)

Неправильно:

{
  "success": true,
  "data": { "orderId": "...", "status": "CREATED" },
  "error": null
}

Правильно:

{
  "orderId": "...",
  "status": "CREATED"
}