Уявіть ситуацію: ви розробляєте сучасний веб-додаток, який має спілкуватися з десятками різних API — отримувати дані про продукти, авторизувати користувачів, завантажувати файли, обробляти помилки мережі. Ви могли б використовувати вбудований Fetch API, але швидко зрозуміли б, що для кожного запиту доводиться писати однотипний код: перевірка статусу, перетворення JSON, обробка таймаутів... Чи є спосіб працювати з HTTP-запитами елегантніше?
Axios — це саме те рішення, яке обрали мільйони розробників по всьому світу. Ця бібліотека перетворює роботу з HTTP на задоволення, надаючи потужний, гнучкий та інтуїтивно зрозумілий API.
Перед вивченням цього матеріалу рекомендуємо ознайомитись з:
Axios (грецькою "ἄξιος" — "вартий", "достойний") — це promise-based HTTP-клієнт для браузера та Node.js. Бібліотека була створена у 2014 році як відповідь на потребу в зручнішому способі виконання AJAX-запитів.
Ключові компоненти:
Перш ніж обирати інструмент, важливо розуміти відмінності між вбудованим Fetch API та Axios:
| Критерій | Fetch API | Axios |
|---|---|---|
| Вбудований у браузер | ✅ Так | ❌ Потребує встановлення |
| Автоматичне перетворення JSON | ❌ Потрібен .json() | ✅ Автоматично |
| Відхилення промісу при помилках | ❌ Тільки при мережевій помилці | ✅ При статусах 4xx/5xx |
| Скасування запитів | ✅ AbortController | ✅ AbortController |
| Перехоплювачі (Interceptors) | ❌ Немає | ✅ Вбудовані |
| Трансформація даних | ❌ Вручну | ✅ transformRequest/Response |
| Таймаути | ❌ Через AbortController | ✅ Вбудована опція timeout |
| Прогрес завантаження | ❌ Складно | ✅ onUploadProgress/onDownloadProgress |
| XSRF захист | ❌ Вручну | ✅ Вбудований |
| Підтримка Node.js | ❌ Потрібен polyfill | ✅ Нативна |
// Fetch API: потрібно багато ручної роботи
async function getUser(id) {
try {
const response = await fetch(`https://api.escuelajs.co/api/v1/users/${id}`)
// Fetch НЕ кидає помилку при 404, 500 тощо!
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
// Потрібно вручну парсити JSON
const data = await response.json()
return data
} catch (error) {
console.error('Помилка:', error)
throw error
}
}
// Axios: чистий та лаконічний код
async function getUser(id) {
try {
// Автоматичний парсинг JSON та помилки при 4xx/5xx
const { data } = await axios.get(`https://api.escuelajs.co/api/v1/users/${id}`)
return data
} catch (error) {
console.error('Помилка:', error.response?.data || error.message)
throw error
}
}
npm install axios
// Сучасний синтаксис (Vite, Webpack, ESM)
import axios from 'axios'
// Node.js (CommonJS)
const axios = require('axios')
<!-- Підключення через CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// axios доступний глобально
axios.get('/api/data').then((response) => console.log(response.data))
</script>
Axios надає зручні методи для всіх HTTP-операцій. У цьому розділі ми використовуватимемо Platzi Fake Store API — безкоштовний тестовий API для практики.
https://api.escuelajs.co/api/v1/) — це реалістичний e-commerce API з підтримкою CRUD операцій, JWT авторизації та пагінації. Ідеальний для навчання!GET-запити використовуються для отримання даних з сервера. Це найпоширеніший тип запитів.
import axios from 'axios'
// Отримання списку продуктів
async function getProducts() {
try {
const response = await axios.get('https://api.escuelajs.co/api/v1/products')
console.log('Статус:', response.status) // 200
console.log('Заголовки:', response.headers) // об'єкт з заголовками
console.log('Дані:', response.data) // масив продуктів
return response.data
} catch (error) {
console.error('Помилка отримання продуктів:', error.message)
}
}
// Отримання одного продукту за ID
async function getProductById(id) {
try {
const { data } = await axios.get(`https://api.escuelajs.co/api/v1/products/${id}`)
console.log('Продукт:', data.title, '- $' + data.price)
return data
} catch (error) {
if (error.response?.status === 404) {
console.log('Продукт не знайдено')
}
}
}
// Виклик функцій
getProducts()
getProductById(4)
Анатомія відповіді:
Об'єкт response від Axios містить:
response.data — тіло відповіді (автоматично парситься з JSON)response.status — HTTP статус-код (200, 404, 500 тощо)response.statusText — текстовий опис статусу ("OK", "Not Found")response.headers — заголовки відповідіresponse.config — конфігурація, що була використана для запитуresponse.request — об'єкт запиту (XMLHttpRequest у браузері)Для передачі query-параметрів використовуйте опцію params:
import axios from 'axios'
// Пагінація: отримати 10 продуктів, починаючи з 20-го
async function getProductsWithPagination(offset = 0, limit = 10) {
try {
const { data } = await axios.get('https://api.escuelajs.co/api/v1/products', {
params: {
offset: offset, // скільки пропустити
limit: limit, // скільки отримати
},
})
// URL буде: /products?offset=0&limit=10
console.log(`Отримано ${data.length} продуктів`)
return data
} catch (error) {
console.error('Помилка:', error.message)
}
}
// Фільтрація за ціною
async function getProductsByPriceRange(minPrice, maxPrice) {
try {
const { data } = await axios.get('https://api.escuelajs.co/api/v1/products', {
params: {
price_min: minPrice,
price_max: maxPrice,
},
})
// URL буде: /products?price_min=100&price_max=500
console.log(`Знайдено ${data.length} продуктів у ціновому діапазоні`)
return data
} catch (error) {
console.error('Помилка:', error.message)
}
}
// Приклади виклику
getProductsWithPagination(0, 5)
getProductsByPriceRange(100, 500)
POST-запити використовуються для створення нових ресурсів на сервері.
import axios from 'axios'
// Створення нового продукту
async function createProduct(productData) {
try {
const { data } = await axios.post('https://api.escuelajs.co/api/v1/products', {
title: productData.title,
price: productData.price,
description: productData.description,
categoryId: productData.categoryId,
images: productData.images,
})
console.log('Створено продукт з ID:', data.id)
console.log('Slug:', data.slug)
return data
} catch (error) {
if (error.response) {
// Сервер відповів з помилкою
console.error('Помилка валідації:', error.response.data)
} else {
console.error('Помилка мережі:', error.message)
}
}
}
// Приклад використання
createProduct({
title: 'Новий смартфон',
price: 999,
description: 'Потужний смартфон з чудовою камерою',
categoryId: 1,
images: ['https://placehold.co/600x400'],
})
Що відбувається під капотом:
Content-Type: application/jsonPUT — повна заміна ресурсу. PATCH — часткове оновлення.
import axios from 'axios'
// PUT: повне оновлення продукту
async function updateProduct(id, productData) {
try {
const { data } = await axios.put(`https://api.escuelajs.co/api/v1/products/${id}`, productData)
console.log('Оновлено:', data.title)
return data
} catch (error) {
console.error('Помилка оновлення:', error.response?.data || error.message)
}
}
// PATCH: часткове оновлення (тільки ціна)
async function updateProductPrice(id, newPrice) {
try {
const { data } = await axios.patch(
`https://api.escuelajs.co/api/v1/products/${id}`,
{ price: newPrice }, // тільки поле, яке потрібно змінити
)
console.log(`Ціну змінено на $${data.price}`)
return data
} catch (error) {
console.error('Помилка:', error.message)
}
}
// Приклади
updateProduct(1, {
title: 'Оновлена назва',
price: 150,
})
updateProductPrice(1, 199)
import axios from 'axios'
// Видалення продукту
async function deleteProduct(id) {
try {
const { data } = await axios.delete(`https://api.escuelajs.co/api/v1/products/${id}`)
// Platzi API повертає true при успішному видаленні
if (data === true) {
console.log(`Продукт ${id} успішно видалено`)
}
return data
} catch (error) {
if (error.response?.status === 404) {
console.log('Продукт не знайдено')
} else {
console.error('Помилка видалення:', error.message)
}
}
}
deleteProduct(210)
Axios надає гнучку систему конфігурації. Кожен запит може мати власний набір параметрів.
import axios from 'axios'
// Приклад запиту з повною конфігурацією
const config = {
// Обов'язкові
url: '/products',
method: 'get', // 'get' | 'post' | 'put' | 'patch' | 'delete'
// Базовий URL (додається до url)
baseURL: 'https://api.escuelajs.co/api/v1',
// Заголовки запиту
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer your-token-here',
'X-Custom-Header': 'custom-value',
},
// Query параметри (?key=value)
params: {
limit: 10,
offset: 0,
},
// Тіло запиту (для POST, PUT, PATCH)
data: {
title: 'Новий продукт',
price: 100,
},
// Таймаут у мілісекундах
timeout: 5000,
// Тип відповіді: 'json' | 'text' | 'blob' | 'arraybuffer' | 'document' | 'stream'
responseType: 'json',
// Функція валідації статусу
validateStatus: function (status) {
return status >= 200 && status < 300 // За замовчуванням
},
// Максимальна кількість редиректів (Node.js)
maxRedirects: 5,
// Прогрес завантаження
onUploadProgress: function (progressEvent) {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`Завантажено: ${percent}%`)
},
// Прогрес отримання
onDownloadProgress: function (progressEvent) {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`Отримано: ${percent}%`)
},
// Сигнал для скасування запиту
signal: new AbortController().signal,
}
axios(config)
.then((response) => console.log(response.data))
.catch((error) => console.error(error))
| Опція | Тип | Опис |
|---|---|---|
url | string | URL ендпоінту |
method | string | HTTP метод (get, post, put, delete...) |
baseURL | string | Базовий URL, що додається до url |
headers | object | HTTP заголовки |
params | object | Query-параметри для URL |
data | any | Тіло запиту |
timeout | number | Таймаут у мілісекундах (0 = без таймауту) |
responseType | string | Тип даних відповіді |
validateStatus | function | Функція для визначення успішності запиту |
signal | AbortSignal | Сигнал для скасування запиту |
У реальних проєктах часто потрібно працювати з кількома API або мати різні конфігурації для різних частин додатку. Для цього Axios дозволяє створювати інстанси — окремі екземпляри з власними налаштуваннями.
import axios from 'axios'
// Створення інстансу для Platzi Store API
const storeApi = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
// Тепер можна використовувати без повного URL
async function getProducts() {
// URL буде: https://api.escuelajs.co/api/v1/products
const { data } = await storeApi.get('/products')
return data
}
async function getCategories() {
// URL буде: https://api.escuelajs.co/api/v1/categories
const { data } = await storeApi.get('/categories')
return data
}
Ось як організувати API-клієнт у реальному проєкті:
Переваги такої структури:
Axios об'єднує конфігурації в такому порядку (останній перезаписує попередній):
instance.defaults)config параметр методу)import axios from 'axios'
// Глобальні defaults
axios.defaults.baseURL = 'https://api.example.com'
axios.defaults.timeout = 5000
// Defaults інстансу (перезаписують глобальні)
const api = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1', // перезапише глобальний
timeout: 10000, // перезапише глобальний
})
// Конфігурація запиту (перезаписує defaults інстансу)
api.get('/products', {
timeout: 30000, // для цього одного запиту таймаут буде 30 секунд
})
Перехоплювачі — це функції, які автоматично виконуються для кожного запиту або відповіді. Це одна з найпотужніших можливостей Axios.
Виконуються перед відправкою запиту на сервер.
import axios from 'axios'
const api = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1',
})
// Додаємо request interceptor
api.interceptors.request.use(
// onFulfilled: виконується перед запитом
function (config) {
// 1. Додаємо токен авторизації
const token = localStorage.getItem('accessToken')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// 2. Логування запитів (для дебагу)
console.log(`📤 ${config.method.toUpperCase()} ${config.url}`)
// 3. Додаємо timestamp
config.metadata = { startTime: new Date() }
// ОБОВ'ЯЗКОВО повертаємо config!
return config
},
// onRejected: виконується при помилці налаштування запиту
function (error) {
console.error('❌ Помилка налаштування запиту:', error)
return Promise.reject(error)
},
)
Виконуються після отримання відповіді від сервера.
import axios from 'axios'
const api = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1',
})
// Додаємо response interceptor
api.interceptors.response.use(
// onFulfilled: успішна відповідь (2xx)
function (response) {
// 1. Логування часу відповіді
const duration = new Date() - response.config.metadata?.startTime
console.log(`📥 ${response.status} ${response.config.url} (${duration}ms)`)
// 2. Можна трансформувати дані
// response.data = transformData(response.data);
return response
},
// onRejected: помилка (4xx, 5xx, мережеві помилки)
function (error) {
// Отримуємо деталі помилки
const status = error.response?.status
const url = error.config?.url
console.error(`❌ ${status || 'Network'} Error: ${url}`)
// 3. Глобальна обробка типових помилок
if (status === 401) {
console.log('Сесія закінчилась, виконуємо logout...')
localStorage.removeItem('accessToken')
// window.location.href = '/login';
}
if (status === 403) {
console.log('Недостатньо прав доступу')
}
if (status === 500) {
console.log('Помилка сервера, спробуйте пізніше')
}
return Promise.reject(error)
},
)
Одна з найпоширеніших задач — автоматичне оновлення JWT токена при отриманні 401 помилки:
import axios from 'axios'
const api = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1',
})
// Змінна для запобігання повторним оновленням
let isRefreshing = false
let failedQueue = []
const processQueue = (error, token = null) => {
failedQueue.forEach((promise) => {
if (error) {
promise.reject(error)
} else {
promise.resolve(token)
}
})
failedQueue = []
}
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config
// Якщо отримали 401 і це не запит на оновлення токена
if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
// Додаємо запит до черги
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject })
}).then((token) => {
originalRequest.headers.Authorization = `Bearer ${token}`
return api(originalRequest)
})
}
originalRequest._retry = true
isRefreshing = true
try {
const refreshToken = localStorage.getItem('refreshToken')
const { data } = await axios.post('https://api.escuelajs.co/api/v1/auth/refresh-token', {
refreshToken,
})
// Зберігаємо новий токен
localStorage.setItem('accessToken', data.access_token)
localStorage.setItem('refreshToken', data.refresh_token)
// Оновлюємо заголовок
api.defaults.headers.Authorization = `Bearer ${data.access_token}`
originalRequest.headers.Authorization = `Bearer ${data.access_token}`
processQueue(null, data.access_token)
// Повторюємо оригінальний запит
return api(originalRequest)
} catch (refreshError) {
processQueue(refreshError, null)
// Logout користувача
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
return Promise.reject(refreshError)
} finally {
isRefreshing = false
}
}
return Promise.reject(error)
},
)
Іноді потрібно видалити перехоплювач (наприклад, для тестування):
const api = axios.create()
// Зберігаємо ID перехоплювача
const interceptorId = api.interceptors.request.use((config) => {
console.log('Це буде видалено')
return config
})
// Видаляємо перехоплювач
api.interceptors.request.eject(interceptorId)
Правильна обробка помилок — критичний аспект роботи з HTTP-запитами.
import axios from 'axios'
async function fetchData() {
try {
const { data } = await axios.get('https://api.escuelajs.co/api/v1/products/99999')
return data
} catch (error) {
if (error.response) {
// ✅ Сервер відповів з помилкою (4xx, 5xx)
console.log('Статус помилки:', error.response.status)
console.log('Дані помилки:', error.response.data)
console.log('Заголовки:', error.response.headers)
// Обробка конкретних статусів
switch (error.response.status) {
case 400:
console.log('Невірний запит:', error.response.data.message)
break
case 401:
console.log('Необхідна авторизація')
break
case 403:
console.log('Доступ заборонено')
break
case 404:
console.log('Ресурс не знайдено')
break
case 422:
console.log('Помилка валідації:', error.response.data.message)
break
case 500:
console.log('Помилка сервера')
break
default:
console.log('Невідома помилка')
}
} else if (error.request) {
// ✅ Запит надіслано, але відповіді немає
console.log('Мережева помилка - сервер не відповів')
console.log('Request:', error.request)
// Можливі причини:
// - Немає інтернету
// - Сервер недоступний
// - Таймаут
// - CORS блокування
} else {
// ✅ Помилка налаштування запиту
console.log('Помилка конфігурації:', error.message)
}
// Додаткова інформація
console.log('Конфігурація запиту:', error.config)
throw error // Прокидаємо помилку далі
}
}
// Кастомний клас для API помилок
class ApiError extends Error {
constructor(message, status, data) {
super(message)
this.name = 'ApiError'
this.status = status
this.data = data
}
static fromAxiosError(error) {
if (error.response) {
return new ApiError(
error.response.data?.message || error.message,
error.response.status,
error.response.data,
)
}
if (error.request) {
return new ApiError('Мережева помилка', 0, null)
}
return new ApiError(error.message, -1, null)
}
}
// Використання в interceptor
api.interceptors.response.use(
(response) => response,
(error) => Promise.reject(ApiError.fromAxiosError(error)),
)
// Тепер можна обробляти так:
try {
await api.get('/products')
} catch (error) {
if (error instanceof ApiError) {
console.log(`API Error [${error.status}]: ${error.message}`)
}
}
У сучасних додатках часто потрібно скасовувати незавершені запити — наприклад, при переході на іншу сторінку або при введенні нового пошукового запиту.
import axios from 'axios'
// Створюємо контролер
const controller = new AbortController()
// Передаємо signal в запит
axios
.get('https://api.escuelajs.co/api/v1/products', {
signal: controller.signal,
})
.then((response) => {
console.log('Дані:', response.data)
})
.catch((error) => {
if (axios.isCancel(error) || error.name === 'CanceledError') {
console.log('Запит скасовано:', error.message)
} else {
console.error('Помилка:', error.message)
}
})
// Скасовуємо запит через 100мс
setTimeout(() => {
controller.abort()
console.log('Запит скасовано!')
}, 100)
import axios from 'axios'
// Зберігаємо поточний контролер
let searchController = null
async function searchProducts(query) {
// Скасовуємо попередній запит, якщо він ще виконується
if (searchController) {
searchController.abort()
}
// Створюємо новий контролер
searchController = new AbortController()
try {
const { data } = await axios.get('https://api.escuelajs.co/api/v1/products', {
params: { title: query },
signal: searchController.signal,
})
console.log(`Знайдено ${data.length} продуктів для "${query}"`)
return data
} catch (error) {
if (axios.isCancel(error)) {
console.log(`Пошук "${query}" скасовано — новий запит`)
return null // Не обробляємо як помилку
}
throw error
}
}
// Симуляція швидкого введення
searchProducts('iph') // Скасується
searchProducts('ipho') // Скасується
searchProducts('iphon') // Скасується
searchProducts('iphone') // Виконається
import axios from 'axios'
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController()
// Автоматичне скасування через timeout
const timeoutId = setTimeout(() => {
controller.abort()
}, timeoutMs)
try {
const response = await axios.get(url, {
signal: controller.signal,
})
clearTimeout(timeoutId)
return response.data
} catch (error) {
clearTimeout(timeoutId)
if (axios.isCancel(error)) {
throw new Error(`Запит перевищив таймаут ${timeoutMs}мс`)
}
throw error
}
}
// Використання
fetchWithTimeout('https://api.escuelajs.co/api/v1/products', 3000)
.then((data) => console.log('Отримано:', data.length, 'продуктів'))
.catch((error) => console.error(error.message))
timeout, яка автоматично скасовує запит. Проте AbortController дає більше контролю та є стандартом Web API.import axios from 'axios'
// HTML: <input type="file" id="fileInput">
async function uploadFile() {
const fileInput = document.getElementById('fileInput')
const file = fileInput.files[0]
if (!file) {
console.error('Файл не обрано')
return
}
const formData = new FormData()
formData.append('file', file)
try {
const { data } = await axios.post('https://api.escuelajs.co/api/v1/files/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: (progressEvent) => {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`Завантаження: ${percent}%`)
},
})
console.log('Файл завантажено:', data.location)
return data
} catch (error) {
console.error('Помилка завантаження:', error.response?.data || error.message)
}
}
import axios from 'axios'
async function createProductWithImage(productData, imageFile) {
const formData = new FormData()
// Спочатку завантажуємо зображення
formData.append('file', imageFile)
try {
// 1. Завантажуємо зображення
const uploadResponse = await axios.post('https://api.escuelajs.co/api/v1/files/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
const imageUrl = uploadResponse.data.location
// 2. Створюємо продукт з URL зображення
const { data } = await axios.post('https://api.escuelajs.co/api/v1/products', {
...productData,
images: [imageUrl],
})
console.log('Продукт створено:', data)
return data
} catch (error) {
console.error('Помилка:', error.response?.data || error.message)
}
}
import axios from 'axios'
async function loadDashboardData() {
try {
const [productsRes, categoriesRes, usersRes] = await Promise.all([
axios.get('https://api.escuelajs.co/api/v1/products?limit=5'),
axios.get('https://api.escuelajs.co/api/v1/categories'),
axios.get('https://api.escuelajs.co/api/v1/users?limit=5'),
])
return {
products: productsRes.data,
categories: categoriesRes.data,
users: usersRes.data,
}
} catch (error) {
// Якщо хоча б один запит впаде — всі падають
console.error('Помилка завантаження:', error.message)
}
}
import axios from 'axios'
// axios.all = Promise.all
// axios.spread = деструктуризація результатів
axios
.all([
axios.get('https://api.escuelajs.co/api/v1/products?limit=5'),
axios.get('https://api.escuelajs.co/api/v1/categories'),
])
.then(
axios.spread((productsRes, categoriesRes) => {
console.log('Продукти:', productsRes.data)
console.log('Категорії:', categoriesRes.data)
}),
)
import axios from 'axios'
async function loadDataSafely() {
const results = await Promise.allSettled([
axios.get('https://api.escuelajs.co/api/v1/products?limit=5'),
axios.get('https://api.escuelajs.co/api/v1/invalid-endpoint'), // Впаде з 404
axios.get('https://api.escuelajs.co/api/v1/categories'),
])
const data = {
products: null,
invalid: null,
categories: null,
}
results.forEach((result, index) => {
const keys = ['products', 'invalid', 'categories']
if (result.status === 'fulfilled') {
data[keys[index]] = result.value.data
} else {
console.warn(`Запит ${keys[index]} впав:`, result.reason.message)
}
})
return data
}
Об'єднаємо все вивчене у повний практичний приклад:
import axios from 'axios'
// ========================================
// 1. НАЛАШТУВАННЯ API КЛІЄНТА
// ========================================
const api = axios.create({
baseURL: 'https://api.escuelajs.co/api/v1',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
// Request interceptor: логування
api.interceptors.request.use((config) => {
console.log(`📤 ${config.method.toUpperCase()} ${config.url}`)
config.metadata = { startTime: Date.now() }
return config
})
// Response interceptor: логування та обробка помилок
api.interceptors.response.use(
(response) => {
const duration = Date.now() - response.config.metadata.startTime
console.log(`📥 ${response.status} (${duration}ms)`)
return response
},
(error) => {
const status = error.response?.status
console.error(`❌ Error ${status}: ${error.message}`)
return Promise.reject(error)
},
)
// ========================================
// 2. CRUD ОПЕРАЦІЇ
// ========================================
// CREATE - створення продукту
async function createProduct(product) {
try {
const { data } = await api.post('/products', {
title: product.title,
price: product.price,
description: product.description,
categoryId: product.categoryId,
images: product.images || ['https://placehold.co/600x400'],
})
console.log('✅ Створено:', data.id, data.title)
return data
} catch (error) {
console.error('Помилка створення:', error.response?.data)
throw error
}
}
// READ - отримання продуктів
async function getProducts(options = {}) {
const { limit = 10, offset = 0 } = options
try {
const { data } = await api.get('/products', {
params: { limit, offset },
})
console.log(`✅ Отримано ${data.length} продуктів`)
return data
} catch (error) {
console.error('Помилка отримання:', error.message)
throw error
}
}
// READ - отримання одного продукту
async function getProduct(id) {
try {
const { data } = await api.get(`/products/${id}`)
console.log('✅ Продукт:', data.title)
return data
} catch (error) {
if (error.response?.status === 404) {
console.log('Продукт не знайдено')
return null
}
throw error
}
}
// UPDATE - оновлення продукту
async function updateProduct(id, updates) {
try {
const { data } = await api.put(`/products/${id}`, updates)
console.log('✅ Оновлено:', data.title)
return data
} catch (error) {
console.error('Помилка оновлення:', error.response?.data)
throw error
}
}
// DELETE - видалення продукту
async function deleteProduct(id) {
try {
const { data } = await api.delete(`/products/${id}`)
console.log('✅ Видалено:', data)
return data
} catch (error) {
console.error('Помилка видалення:', error.message)
throw error
}
}
// ========================================
// 3. ДЕМОНСТРАЦІЯ
// ========================================
async function demo() {
console.log('\n🚀 Демонстрація CRUD операцій\n')
// 1. Отримати список продуктів
console.log('--- READ (список) ---')
const products = await getProducts({ limit: 3 })
// 2. Отримати один продукт
console.log('\n--- READ (один) ---')
const product = await getProduct(4)
// 3. Створити новий продукт
console.log('\n--- CREATE ---')
const newProduct = await createProduct({
title: 'Тестовий продукт',
price: 999,
description: 'Створено через Axios',
categoryId: 1,
})
// 4. Оновити продукт
console.log('\n--- UPDATE ---')
if (newProduct) {
await updateProduct(newProduct.id, {
title: 'Оновлений продукт',
price: 1299,
})
}
// 5. Видалити продукт
console.log('\n--- DELETE ---')
if (newProduct) {
await deleteProduct(newProduct.id)
}
console.log('\n✨ Демонстрація завершена!\n')
}
// Запуск
demo()
Створюйте інстанси
axios напряму. Створюйте інстанси для кожного API для кращого контролю та чистоти коду.Централізуйте обробку помилок
Типізуйте відповіді
d.ts файли для документування структур відповідей вашого API.Скасовуйте запити
AbortController для скасування запитів при розмонтуванні компонентів або зміні сторінок.| Погано | Добре |
|---|---|
| Ігнорувати помилки | Завжди обробляти catch блок |
| Хардкодити URL | Використовувати baseURL та конфіги |
| Дублювати headers | Налаштувати їх в інстансі |
| Не скасовувати запити | Використовувати AbortController |
| Логувати credentials | Ховати чутливі дані |
// ❌ Погано
axios.get('https://api.example.com/users').then((res) => console.log(res.data))
// Немає обробки помилок!
// ✅ Добре
api.get('/users')
.then((res) => setUsers(res.data))
.catch((error) => {
setError(error.message)
console.error('Помилка:', error)
})
Закріпіть отримані знання, пройшовши короткий тест:
js-cookie: Керування Cookies без Болю
У попередніх розділах ми дослідили нативний інтерфейс document.cookie. Ви напевно помітили, наскільки він архаїчний та незручний. Робота з ним нагадує спробу написати SMS, використовуючи азбуку Морзе — це можливо, але навіщо так страждати у 21-му столітті?
LocalStorage, SessionStorage та patterns збереження даних
Детальний посібник з Web Storage API. Більше 10 реальних паттернів використання (Auth, Caching, Theming, State Sync) та архітектурні рішення.