HTTP клиент на JavaScript: делаем запросы правильно — от А до Я
В современной веб-разработке взаимодействие с внешними API и серверами — рутинная, но критически важная задача. Независимо от того, пишете ли вы микросервис на Node.js, парсите данные или создаете бота, вам понадобится надежный HTTP клиент. В этой статье мы разберем, как делать HTTP запросы на JavaScript (Node.js) правильно, рассмотрим популярные библиотеки, обработку ошибок, таймауты и лучшие практики.
1. Встроенные средства: модуль http и node-fetch
Node.js имеет встроенный модуль http и https, но их API низкоуровневый и неудобный для повседневного использования. Поэтому чаще всего используют библиотеки-обертки.
1.1. node-fetch (v3+)
Начиная с версии 18, Node.js имеет встроенную поддержку fetch (экспериментально). Для более старых версий или расширенного функционала используйте пакет node-fetch.
// Установка: npm install node-fetch
import fetch from 'node-fetch';
async function getUsers() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Ошибка запроса:', error.message);
}
}
getUsers();
1.2. Встроенный fetch (Node.js 18+)
Используется аналогично браузерному fetch:
// Node.js 18+ без дополнительных пакетов
async function postData() {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
});
return response.json();
}
2. Axios — золотой стандарт HTTP клиента
Axios — самая популярная библиотека для HTTP запросов в JavaScript. Она предоставляет удобный интерфейс, поддержку промисов, перехватчики (interceptors) и автоматическую трансформацию JSON.
2.1. Установка и базовый GET-запрос
// npm install axios
const axios = require('axios');
async function fetchUsers() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
console.log(response.data); // данные уже распаршены
} catch (error) {
console.error('Ошибка:', error.message);
}
}
2.2. POST-запрос с данными
async function createUser() {
const newUser = { name: 'Иван', email: 'ivan@example.com' };
const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
console.log('Создан пользователь:', response.data);
}
2.3. Настройка таймаута и заголовков
Таймаут — критически важная опция для предотвращения зависания приложения:
async function fetchWithTimeout() {
try {
const response = await axios.get('https://slow-api.example.com/data', {
timeout: 5000, // 5 секунд
headers: {
'Authorization': 'Bearer token123',
'Accept': 'application/json'
}
});
return response.data;
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.error('Таймаут запроса');
}
}
}
3. Продвинутые техники и лучшие практики
3.1. Обработка ошибок и повторные попытки (retry)
Сети нестабильны, поэтому важно реализовать механизм повторных попыток:
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await axios.get(url);
return response.data;
} catch (error) {
if (i === retries - 1) throw error; // последняя попытка
console.log(`Попытка ${i + 1} не удалась, повтор через ${(i + 1) * 1000}ms...`);
await new Promise(resolve => setTimeout(resolve, (i + 1) * 1000));
}
}
}
3.2. Использование Interceptors (перехватчиков)
Перехватчики позволяют выполнять код до отправки запроса или после получения ответа — идеально для логирования, добавления токенов или обработки ошибок:
// Добавляем перехватчик для логирования
axios.interceptors.request.use(config => {
console.log(`[${config.method.toUpperCase()}] ${config.url}`);
return config;
}, error => Promise.reject(error));
// Перехватчик для обработки 401 ошибки
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
console.log('Неавторизован. Перенаправляем на страницу входа.');
// Логика обновления токена или редиректа
}
return Promise.reject(error);
}
);
3.3. Отмена запроса (AbortController)
Иногда нужно отменить запрос, например, если пользователь покинул страницу или ввел новый поисковый запрос:
const { AbortController } = require('node-abort-controller'); // для старых версий
async function searchUsers(query) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // авто-отмена через 5с
try {
const response = await axios.get(`/api/users?q=${query}`, {
signal: controller.signal
});
clearTimeout(timeoutId);
return response.data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Запрос был отменен');
}
}
}
4. Сравнение популярных HTTP клиентов
- Axios — самый функциональный, удобный, поддержка interceptors, отмены запросов, прогресса загрузки.
- node-fetch — легковесный, соответствует браузерному fetch API, хорош для простых задач.
- Got — мощная альтернатива, поддерживает потоки, кэширование, retry "из коробки".
- SuperAgent — гибкий, но менее популярный, чем Axios.
5. Практический пример: создание универсального HTTP клиента
Соберем все лучшие практики в один класс:
class HttpClient {
constructor(baseURL = '') {
this.client = axios.create({
baseURL,
timeout: