REST API Style Guide: JSON и формат ответов
Именование полей в JSON. Формат ответов REST API: ресурсы, коллекции, null-поля.
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"
}