Stage 3. JSON in API
JSON в API нужно воспринимать как внешний договор между клиентом и сервером. Этот договор читают не только разработчики backend, но и frontend, мобильные приложения, автотесты, партнёрские интеграции и аналитика. Если договор неявный, даже маленькое изменение может сломать чужую часть системы. Поэтому JSON проектируют как стабильный интерфейс: с понятными полями, предсказуемыми типами и прозрачными правилами ошибок.
1. Что такое JSON простыми словами
JSON — это текстовый формат данных с объектами {}, массивами [] и парами ключ: значение. Его главное преимущество в API: один и тот же payload одинаково понятен системам на Java, JavaScript, Python, Go и других языках. Клиенту не нужно знать внутреннее устройство сервера, ему нужно знать только структуру JSON-контракта.
Для старта важно не путать формат и смысл. Формат отвечает на вопрос «это корректный JSON-текст?». Смысл отвечает на вопрос «эти данные подходят бизнес-правилам API?». Например, строка "twenty" в поле age может быть корректной как JSON, но неверной по правилам вашего сервиса, если ожидается число.
2. Минимальные правила синтаксиса, которые чаще всего ломают запросы
- Ключи всегда в двойных кавычках:
"email", а неemail. - Строки тоже в двойных кавычках:
"Anna", а не'Anna'. - Запятая разделяет элементы, но не ставится после последнего поля.
- Типы должны совпадать с контрактом: число, строка, булево, массив, объект.
null— это явное значение «пусто», а не «поле отсутствует».
3. Базовые типы JSON и как их читать в API
{
"id": 42,
"name": "Anna",
"isActive": true,
"tags": ["backend", "java"],
"profile": {
"city": "Kyiv"
},
"middleName": null
}
| Тип | Пример | Практический смысл |
|---|---|---|
string | "[email protected]" | Текст, например email или имя |
number | 27 | Числа для возраста, цены, количества |
boolean | true | Флаг включено/выключено |
array | ["a", "b"] | Список значений |
object | {"city":"Kyiv"} | Группа связанных полей |
null | null | Значение явно отсутствует |
4. Как JSON проходит через сервер
Чтобы новичку было проще отлаживать API, удобно помнить простой поток обработки:
flowchart LR
A[HTTP Request with JSON] --> B[Parse JSON]
B --> C[Validate Fields and Types]
C --> D[Run Business Rules]
D --> E[JSON Response or Error]
Если ошибка произошла на этапе парсинга, клиент получает ответ о неверном формате. Если парсинг успешный, но не пройдена валидация, клиент получает ошибку по конкретным полям. Если валидация прошла, запускается бизнес-логика. Такое разделение делает ответы API предсказуемыми и удобными для автоматической обработки.
5. Разделяйте внутренние модели и внешний контракт (DTO)
Внутри сервиса модели меняются часто: добавляются технические поля, меняется хранение, пересобираются связи. Внешний API должен меняться осторожно. Если отдавать внутренние сущности напрямую, любое внутреннее изменение превращается в риск для клиентов. DTO решают эту проблему: вы явно описываете, что именно доступно снаружи и какие поля являются частью договора.
6. Единый формат ошибок
{
"error": "validation_failed",
"fields": {
"email": "must be valid"
},
"traceId": "d1f1c9a2"
}
Одинаковая структура ошибок нужна для всех endpoint-ов. Тогда frontend и mobile не пишут уникальную обработку под каждую ручку, а используют общий механизм: показать ошибку, подсветить поле, записать traceId для логов. Это ускоряет разработку и упрощает поддержку.
7. Дата и время: главное правило для интеграций
Самая частая скрытая проблема в API — разный формат времени в разных ответах. Один endpoint возвращает UTC, другой локальное время без timezone, третий строку в произвольном формате. В итоге отчёты по регионам расходятся, а фильтры «за последние 24 часа» работают по-разному. Для начинающего разработчика полезно зафиксировать правило сразу: используйте единый формат времени для всех endpoint-ов, обычно ISO 8601 в UTC.
8. Как безопасно развивать контракт
- Новые поля добавляйте как необязательные.
- Не меняйте тип существующего поля без миграции.
- Не удаляйте обязательные поля сразу, дайте переходный период.
- Фиксируйте версию API, если изменения потенциально несовместимы.
9. Проверочный список для новичка перед публикацией endpoint-а
- Все ли поля имеют понятные названия без знания внутреннего кода?
- Есть ли явные правила для обязательных и опциональных полей?
- Одинаков ли формат ошибок на всех ручках?
- Единообразен ли формат даты и времени?
- Есть ли тесты не только на успешный сценарий, но и на ошибки?
10. Как один и тот же JSON читается в Java и Python
Возьмем простой ответ API:
{
"userId": 42,
"email": "[email protected]",
"active": true
}
В Java часто используют Jackson для чтения JSON в объект DTO. Так проще работать с типами и валидацией на уровне кода.
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonReadExample {
static class UserDto {
public int userId;
public String email;
public boolean active;
}
public static void main(String[] args) throws Exception {
String json = "{\"userId\":42,\"email\":\"[email protected]\",\"active\":true}";
ObjectMapper mapper = new ObjectMapper();
UserDto user = mapper.readValue(json, UserDto.class);
System.out.println(user.userId); // 42
System.out.println(user.email); // [email protected]
System.out.println(user.active); // true
}
}
В Python то же самое обычно читают через стандартный модуль json. На выходе получается словарь (dict), откуда значения берутся по ключам.
import json
raw = '{"userId": 42, "email": "[email protected]", "active": true}'
data = json.loads(raw)
print(data["userId"]) # 42
print(data["email"]) # [email protected]
print(data["active"]) # True
Главная идея для новичка: JSON-контракт один и тот же, а способ чтения зависит от языка и библиотек. Поэтому стабильная структура полей важнее, чем конкретный стек клиента.
Практический сценарий
Команда добавила поле createdAt в локальном времени без timezone. На одном стенде всё выглядело правильно, но в отчётах по нескольким регионам данные «уехали». Выяснилось, что часть сервисов интерпретировала время как UTC, а часть как локальное. Ошибка была не в бизнес-логике, а в плохо заданном JSON-контракте. Если бы формат времени был заранее стандартизирован и описан для всех клиентов, инцидента можно было бы избежать.