Stage 3. JSON in API
В API JSON — это не просто формат обмена, а внешний договор между командами и продуктами. Когда этот договор неуправляем, страдают не только разработчики, но и пользователи: обновления мобильного приложения начинают конфликтовать с сервером, интеграции партнёров ломаются после “безобидных” изменений, аналитика получает несогласованные данные. Поэтому JSON нужно проектировать как стабильный интерфейс, а не как случайный слепок внутренней модели.
Если объяснять JSON максимально просто, это текстовый формат, где данные представляются как пары ключ: значение, объекты {} и массивы []. Практически это означает, что контракт можно читать глазами в любом языке программирования, а не только в JavaScript. Для новичка особенно важно помнить два строгих правила синтаксиса: ключи в JSON всегда строковые и пишутся в двойных кавычках, а запятые разделяют элементы, но не ставятся “после последнего поля”. Эти детали кажутся мелочами, но именно они чаще всего ломают интеграции на первом этапе.
Первая практическая дисциплина — разделять внутренние объекты и внешние DTO. Внутри сервиса структура может меняться часто: добавляются поля, оптимизируется хранение, перерабатываются доменные сущности. Внешний контракт должен меняться медленнее и предсказуемее. Если возвращать внутренние структуры напрямую, вы теряете контроль над API-эволюцией.
Вторая дисциплина — ясно отделять этапы обработки входа. Сначала JSON парсится и проверяется на корректный формат. Затем проходят правила валидации. Только после этого данные передаются в бизнес-логику. Когда эти шаги смешаны, клиенту трудно понять, почему запрос отклонён: из-за неверного типа поля, отсутствующего обязательного значения или доменного ограничения.
{
"email": "[email protected]",
"age": 27
}
Полезно различать “валидный JSON” и “валидные бизнес-данные”. Например, {\"age\": \"twenty\"} может быть корректным JSON как текст, но некорректным для вашего API, если ожидается число. Такое разделение делает обработку ошибок понятной: синтаксические ошибки относятся к формату, а бизнес-ошибки — к правилам предметной области.
Формат ошибок должен быть единым и понятным. Это важно не меньше, чем формат успешного ответа. Когда структура ошибок стабильна, фронтенд и мобильные клиенты могут обрабатывать сбои системно, а не через набор исключений для каждого endpoint-а.
{
"error": "validation_failed",
"fields": {
"email": "must be valid"
},
"traceId": "d1f1c9a2"
}
Отдельная тема — поля даты и времени. Самый частый источник скрытых дефектов — разные форматы времени в разных частях API. Если один endpoint возвращает UTC, другой локальное время без timezone, а третий текстовую дату, то фильтрация, отчёты и кросс-сервисные вычисления неизбежно расходятся. Такие ошибки редко ловятся быстро, потому что проявляются как “странная аналитика”, а не как явный crash.
Эволюция JSON-контракта должна быть управляемой. Безопасный путь: новые поля вводить как опциональные, старые обязательные поля удалять только после переходного периода, типы полей не менять внезапно. Для критичных API полезно фиксировать версионную политику заранее, чтобы и сервер, и клиенты двигались синхронно.
Важный практический момент: хороший JSON-контракт должен быть читаем человеком. Если поле трудно интерпретировать без чтения кода сервера, значит контракт слишком тесно связан с внутренней реализацией. Простые, явные имена и стабильные типы обычно выигрывают у “умной” компактности.
Отдельное прикладное правило из практики проектирования API: не старайтесь делать “универсальную JSON-схему на все случаи”. Один и тот же объект в разных сценариях обычно имеет разные представления: список, карточка детали, создание, редактирование. Когда каждая операция получает собственную целевую схему ответа/запроса, API проще расширять без каскадных поломок в клиентах.
Тестирование JSON лучше строить не только вокруг happy-path. Нужны проверки на невалидный тип, отсутствие обязательного поля, лишние поля, а также регрессионные тесты структуры ответа. Даже базовые snapshot-тесты на ключевые endpoint-ы защищают от случайных breaking changes при рефакторинге.
Практический сценарий
Команда добавляет поле createdAt в формате локального времени без timezone. Веб-интерфейс кажется корректным, но отчёты по регионам начинают показывать расхождения. После расследования выясняется, что часть сервисов трактует время как UTC, часть как локальное. Проблема не в бизнес-логике, а в нестрогом JSON-контракте. Если бы формат времени был единообразно определён заранее, инцидента можно было избежать.