JSON — основной формат обмена данными в REST API. Корректная работа с JSON на клиенте и сервере включает парсинг, валидацию структуры и обработку ошибок. В этой статье рассмотрены практические приёмы и рекомендации.
Парсинг JSON на клиенте
fetch
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
response.json() возвращает Promise и парсит тело ответа. При невалидном JSON будет выброшено исключение.
Обработка ошибок парсинга
try {
const data = await response.json();
return data;
} catch (e) {
if (e instanceof SyntaxError) {
console.error('Invalid JSON in response');
}
throw e;
}
Проверка Content-Type
Перед вызовом response.json() можно проверить заголовок:
const contentType = response.headers.get('Content-Type');
if (!contentType?.includes('application/json')) {
throw new Error('Expected JSON response');
}
Валидация структуры
Парсинг проверяет только синтаксис. Структура данных может не соответствовать ожиданиям. Варианты валидации:
Ручная проверка
function parseUser(data) {
if (!data || typeof data !== 'object') {
throw new Error('Invalid user object');
}
if (typeof data.id !== 'number' || typeof data.name !== 'string') {
throw new Error('Missing or invalid fields');
}
return data;
}
Схемы (JSON Schema, Zod, Yup)
Zod (TypeScript/JavaScript):
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email().optional(),
});
const data = UserSchema.parse(jsonData);
При несоответствии схеме parse выбросит ошибку с описанием.
Серверная валидация
На бэкенде всегда валидируйте входящий JSON:
- Проверяйте
Content-Type: application/json - Ограничивайте размер тела запроса
- Валидируйте структуру перед использованием
- Возвращайте понятные ошибки (400 Bad Request с описанием)
Обработка больших ответов
Для больших JSON-ответов рассмотрите:
Инструменты
JSON Formatter на rechecker.ru — валидация и форматирование JSON. Удобно для отладки ответов API.
Типичные проблемы
Пустой ответ
Некоторые API возвращают 204 No Content или пустое тело при успехе. Вызов response.json() на пустом теле вызовет ошибку. Проверяйте response.ok и наличие тела.
Нестандартные форматы
Даты, большие числа — JSON не имеет типов для них. Даты приходят строками (ISO 8601), большие числа могут терять точность в JavaScript. Используйте библиотеки (например, json-bigint) при необходимости.
Кодировка
JSON должен быть в UTF-8. Убедитесь, что сервер отдаёт корректный Content-Type: application/json; charset=utf-8.
Формат ошибок API
Рекомендуемая структура ошибки в JSON:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": [
{ "field": "email", "message": "Must be a valid email" }
]
}
}
Единый формат упрощает обработку на клиенте.
Пагинация
Типичные форматы:
{
"data": [...],
"meta": {
"page": 1,
"perPage": 20,
"total": 150
}
}
Или cursor-based:
{
"data": [...],
"nextCursor": "eyJpZCI6MjF9"
}
Дополнительные материалы
Типичные синтаксические ошибки и их исправление — в Ошибки JSON: как найти и исправить некорректный JSON. Общее руководство по валидации — в JSON валидация и форматирование.
Обработка потоков
Для больших ответов можно использовать потоковое чтение:
const response = await fetch('/api/stream');
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Обработка полных JSON-объектов из buffer (NDJSON)
}
Content Negotiation
Клиент может запрашивать JSON через заголовок Accept:
Accept: application/json
Сервер может вернуть другой формат (XML, HTML) при отсутствии Accept или указании других типов. Всегда проверяйте Content-Type ответа.
Размер тела запроса
Ограничивайте размер входящего JSON на сервере. В Express: express.json({ limit: '1mb' }). Большие payload могут использоваться для DoS-атак.
Безопасность парсинга
Избегайте eval() для парсинга JSON — это выполнение кода. Используйте только JSON.parse(). При десериализации из ненадёжных источников проверяйте структуру на наличие неожиданных полей (prototype pollution).
Отладка в браузере
При отладке API-запросов в DevTools (Network) можно просматривать ответ в формате JSON. Для копирования и форматирования используйте JSON Formatter.
Retry и экспоненциальная задержка
При временных сбоях (503, таймауты) реализуйте retry с экспоненциальной задержкой:
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const res = await fetch(url, options);
if (res.ok) return res;
if (res.status >= 500 && i < maxRetries - 1) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
continue;
}
return res;
} catch (e) {
if (i === maxRetries - 1) throw e;
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
}
}
}
Кэширование ответов
Для редко меняющихся данных кэшируйте JSON в памяти или localStorage. Учитывайте заголовки Cache-Control, ETag для HTTP-кэширования.
Типизация в TypeScript
При работе с API в TypeScript определяйте интерфейсы для ответов:
interface UserResponse {
id: number;
name: string;
email?: string;
}
const data: UserResponse = await response.json();
Это не заменяет runtime-валидацию, но улучшает автодополнение и отлов ошибок на этапе компиляции.