Пошук уроків, статей та іншого контенту
Передаватимете JSON у тілі запиту та перетворюватимете JSON-відповіді на JavaScript-об’єкти.
JSON (JavaScript Object Notation) — текстовий формат для обміну даними між клієнтом і сервером. Він часто використовується в HTTP-запитах і відповідях API.
Приклад JSON-документа:
{
"id": 42,
"name": "Марія",
"isActive": true,
"roles": ["user", "editor"],
"address": null
}JSON підтримує такі типи значень:
об’єкти;
масиви;
рядки;
числа;
true;
false;
null.
У JSON не можна використовувати:
коментарі;
одинарні лапки для рядків;
undefined;
функції;
NaN і Infinity;
змінні або вирази JavaScript.
Наприклад, це JavaScript-об’єкт:
const user = {
name: 'Марія',
isActive: true,
};А це його JSON-представлення:
{
"name": "Марія",
"isActive": true
}Зверніть увагу: у JSON назви властивостей і рядкові значення записуються в подвійних лапках.
Щоб підготувати об’єкт для відправлення в тілі HTTP-запиту, використовуйте JSON.stringify():
const user = {
name: 'Марія',
email: 'maria@example.com',
age: 28,
};
const json = JSON.stringify(user);
console.log(json);
// {"name":"Марія","email":"maria@example.com","age":28}Результатом JSON.stringify() завжди є рядок.
Це важливо, тому що тіло HTTP-запиту передається як набір байтів, а JSON у цьому випадку є текстовим форматом.
За замовчуванням JSON не містить зайвих пробілів:
const data = {
name: 'Марія',
skills: ['JavaScript', 'HTML'],
};
console.log(JSON.stringify(data));
// {"name":"Марія","skills":["JavaScript","HTML"]}Для форматування можна передати третій аргумент:
console.log(JSON.stringify(data, null, 2));Результат:
{
"name": "Марія",
"skills": [
"JavaScript",
"HTML"
]
}Третій аргумент 2 означає кількість пробілів для відступу. Форматування зручне для логів і файлів, але зазвичай не потрібне для мережевих запитів.
Для відправлення JSON зазвичай використовують метод POST, PUT або PATCH.
Приклад запиту за допомогою fetch:
const user = {
name: 'Марія',
email: 'maria@example.com',
};
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(user),
});
console.log(response.status);Тут важливі дві частини:
headers: {
'Content-Type': 'application/json',
}Цей заголовок повідомляє серверу, що тіло запиту містить JSON.
body: JSON.stringify(user)fetch не перетворює JavaScript-об’єкт на JSON автоматично. Якщо передати об’єкт без JSON.stringify(), запит буде сформовано неправильно.
async function createUser(user) {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(user),
});
if (!response.ok) {
throw new Error(`Помилка створення користувача: ${response.status}`);
}
return response.json();
}
async function main() {
try {
const createdUser = await createUser({
name: 'Марія',
email: 'maria@example.com',
});
console.log('Створений користувач:', createdUser);
} catch (error) {
console.error(error.message);
}
}
main();Заголовок Accept повідомляє серверу, що клієнт очікує JSON у відповіді. Він не замінює Content-Type:
Content-Type описує формат тіла поточного запиту;
Accept описує формати, які клієнт може прийняти у відповіді.
Метод fetch() повертає об’єкт Response. Тіло відповіді не є готовим JavaScript-об’єктом.
Щоб прочитати JSON, викличте response.json():
const response = await fetch('/api/users/42');
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const user = await response.json();
console.log(user.name);
console.log(user.email);Метод response.json() асинхронний і повертає проміс. Тому його потрібно викликати з await або обробити через .then().
Еквівалент із промісами:
fetch('/api/users/42')
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
return response.json();
})
.then((user) => {
console.log(user.name);
})
.catch((error) => {
console.error(error.message);
});Нижче наведено приклад функцій для завантаження та створення завдань:
async function getTasks() {
const response = await fetch('/api/tasks', {
headers: {
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`Не вдалося отримати завдання: ${response.status}`);
}
return response.json();
}
async function createTask(title) {
const response = await fetch('/api/tasks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
title,
completed: false,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => null);
const message = errorData?.message ?? 'Не вдалося створити завдання';
throw new Error(message);
}
return response.json();
}
async function main() {
try {
const tasks = await getTasks();
console.log('Усі завдання:', tasks);
const newTask = await createTask('Вивчити роботу з JSON');
console.log('Нове завдання:', newTask);
} catch (error) {
console.error(error.message);
}
}
main();У цьому прикладі:
getTasks() відправляє GET-запит.
createTask() відправляє JSON у тілі POST-запиту.
response.json() перетворює JSON-відповідь на JavaScript-значення.
response.ok перевіряє, чи має статус відповіді значення від 200 до 299.
У разі помилки код намагається прочитати повідомлення сервера.
Для ручного перетворення JSON-рядка використовуйте JSON.parse():
const json = '{"name":"Марія","age":28}';
const user = JSON.parse(json);
console.log(user.name);
console.log(user.age);JSON.parse() може повертати не лише об’єкт. JSON-рядок може описувати масив, число, логічне значення або null:
const numbers = JSON.parse('[1, 2, 3]');
const isEnabled = JSON.parse('true');
const emptyValue = JSON.parse('null');
console.log(numbers); // [1, 2, 3]
console.log(isEnabled); // true
console.log(emptyValue); // nullЯкщо рядок має неправильний формат, виникне SyntaxError:
try {
const data = JSON.parse('{invalid json}');
console.log(data);
} catch (error) {
console.error('Некоректний JSON:', error.message);
}Ненадійні або зовнішні JSON-дані краще розбирати в try...catch.
JSON.stringify()Під час серіалізації деякі значення обробляються особливим чином.
undefined, функції та символиВластивості зі значеннями undefined, функціями або символами пропускаються:
const data = {
name: 'Марія',
password: undefined,
log() {
console.log('log');
},
};
console.log(JSON.stringify(data));
// {"name":"Марія"}У масивах такі значення перетворюються на null:
const values = [1, undefined, () => {}, 4];
console.log(JSON.stringify(values));
// [1,null,null,4]Тому не варто покладатися на JSON як на точну копію будь-якого JavaScript-об’єкта.
Об’єкт Date перетворюється на рядок у форматі ISO:
const data = {
createdAt: new Date('2025-01-15T10:30:00Z'),
};
const json = JSON.stringify(data);
console.log(json);
// {"createdAt":"2025-01-15T10:30:00.000Z"}Після JSON.parse() це буде звичайний рядок, а не об’єкт Date:
const parsed = JSON.parse(json);
console.log(typeof parsed.createdAt);
// stringЯкщо потрібен об’єкт Date, його потрібно створити явно:
const date = new Date(parsed.createdAt);
console.log(date instanceof Date);
// trueJSON не має окремого типу для BigInt. Спроба серіалізувати BigInt спричинить помилку:
const data = {
value: 123n,
};
// TypeError
JSON.stringify(data);Якщо потрібно передати дуже велике число, його часто передають як рядок:
const data = {
value: '123456789012345678901234567890',
};
const json = JSON.stringify(data);
console.log(json);Не кожна HTTP-відповідь містить JSON. Наприклад, сервер може відповісти статусом 204 No Content, у якого немає тіла.
У такому випадку виклик response.json() може завершитися помилкою:
const response = await fetch('/api/tasks/42', {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`Не вдалося видалити завдання: ${response.status}`);
}
if (response.status !== 204) {
const result = await response.json();
console.log(result);
}Якщо формат відповіді може відрізнятися, можна перевірити заголовок Content-Type:
async function readResponse(response) {
if (response.status === 204) {
return null;
}
const contentType = response.headers.get('content-type') ?? '';
if (!contentType.includes('application/json')) {
return response.text();
}
return response.json();
}Метод response.json() і метод response.text() читають тіло відповіді. Одну й ту саму відповідь не можна безпосередньо прочитати обома методами:
const response = await fetch('/api/data');
const json = await response.json();
// Повторне читання response.text() для цієї відповіді вже недоступнеJSON сам по собі не виконує код. Наприклад, рядок у JSON не стає JavaScript-кодом лише через JSON.parse():
const data = JSON.parse('{"value":"alert(1)"}');
console.log(data.value);
// alert(1) — звичайний рядокДля розбору JSON використовуйте JSON.parse(), а не eval():
// Неправильно: eval може виконати довільний код
// const data = eval(json);Також не слід автоматично довіряти структурі відповіді. Сервер може повернути пропущене поле або значення іншого типу:
const data = await response.json();
if (!Array.isArray(data.items)) {
throw new Error('Некоректний формат відповіді');
}Для складніших застосунків структуру відповіді додатково перевіряють схемами або власними функціями валідації.
Під час роботи з JSON зазвичай відбувається така послідовність:
JavaScript-програма створює об’єкт.
JSON.stringify() перетворює об’єкт на рядок.
Рядок передається в тілі HTTP-запиту.
Сервер читає JSON і перетворює його на власну структуру даних.
Сервер формує JSON-відповідь.
response.json() перетворює відповідь на JavaScript-значення.
Схематично:
JavaScript-об’єкт
↓ JSON.stringify()
JSON-рядок у запиті
↓
Сервер
↓
JSON-рядок у відповіді
↓ response.json()
JavaScript-об’єктJSON.stringify()Неправильно:
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
name: 'Марія',
},
});Правильно:
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Марія',
}),
});Content-TypeЯкщо тіло запиту містить JSON, додайте відповідний заголовок:
headers: {
'Content-Type': 'application/json',
}Інакше сервер може сприйняти тіло як звичайний текст або не змогти його розібрати.
JSON.parse() для вже розібраного об’єктаНеправильно:
const user = await response.json();
const parsedUser = JSON.parse(user);response.json() уже повертає JavaScript-значення. Повторний виклик JSON.parse() спричинить помилку.
response.okfetch() не відхиляє проміс автоматично для HTTP-статусів 400 або 500. Помилкою мережі вважається, наприклад, відсутність з’єднання, але HTTP-помилку потрібно перевіряти самостійно:
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`Запит завершився зі статусом ${response.status}`);
}Перед response.json() переконайтеся, що відповідь має тіло та містить JSON. Особливо це важливо для статусу 204.
JSON — текстовий формат обміну даними між клієнтом і сервером.
JSON.stringify() перетворює JavaScript-значення на JSON-рядок.
JSON.parse() перетворює JSON-рядок на JavaScript-значення.
Для JSON у тілі запиту потрібно вказувати Content-Type: application/json.
Об’єкт у body потрібно передавати через JSON.stringify().
Відповідь fetch() перетворюється на JavaScript-об’єкт через await response.json().
fetch() не вважає статуси 4xx і 5xx помилкою автоматично, тому перевіряйте response.ok.
Не всі відповіді містять JSON: враховуйте порожні відповіді та перевіряйте Content-Type.
JSON не зберігає всі особливості JavaScript: функції, undefined, BigInt і тип Date потребують окремої уваги.