Работа с сетью и HTTP API в JavaScript (Node.js): подробное руководство
Что это такое и зачем? (аналогия из реальной жизни)
Представь, что ты сидишь в кафе и хочешь заказать пиццу. Ты не идёшь на кухню и не готовишь её сам — ты просто зовёшь официанта, говоришь ему свой заказ, а через некоторое время он приносит тебе готовую пиццу. В этом простом действии скрыта целая философия взаимодействия между программами.
Работа с сетью и HTTP API — это ровно то же самое, только вместо официанта у нас HTTP-протокол, вместо кухни — удалённый сервер, а вместо пиццы — данные (например, список фильмов, прогноз погоды или информация о пользователе).
В реальной жизни мы постоянно пользуемся такими "официантами": когда открываешь приложение погоды, оно отправляет запрос на сервер и получает данные. Когда ищешь товар на маркетплейсе — браузер просит сервер показать результаты. Даже когда ты читаешь эту статью, твой браузер уже выполнил десятки HTTP-запросов, чтобы загрузить текст, картинки и шрифты.
Зачем это нужно программисту? Потому что современные приложения редко работают в одиночку. Они общаются с другими программами через интернет. Твой сайт может попросить сервер Google Maps показать карту, сервер погоды — дать температуру, а сервер банка — проверить баланс. Всё это происходит через HTTP API.
💡 Важное замечание: HTTP API — это не какой-то один язык или технология. Это просто набор правил (протокол), по которому программы общаются через интернет. JavaScript, Python, Java — все они умеют работать с HTTP API, просто синтаксис разный.
Теория: как это работает изнутри
Давай заглянем "под капот" и разберёмся, что на самом деле происходит, когда твоя программа делает HTTP-запрос.
Клиент-серверная архитектура
В любой сетевой коммуникации есть две стороны:
- Клиент — тот, кто просит (твоя программа на Node.js)
- Сервер — тот, кто отвечает (удалённый компьютер с данными)
HTTP-запрос (Request)
Когда клиент хочет что-то получить, он отправляет запрос, который состоит из:
- Метода — что именно мы хотим сделать (GET — прочитать, POST — создать, PUT — обновить, DELETE — удалить)
- URL — адрес, по которому находится ресурс (например,
https://api.weather.com/v1/current) - Заголовков (Headers) — дополнительная информация (тип данных, авторизация, язык)
- Тела (Body) — данные, которые мы отправляем (обычно в POST или PUT запросах)
HTTP-ответ (Response)
Сервер обрабатывает запрос и возвращает ответ, который содержит:
- Статус-код — трёхзначное число, которое говорит, всё ли хорошо:
200— OK (успех)201— Created (создано)400— Bad Request (неправильный запрос)404— Not Found (не найдено)500— Internal Server Error (ошибка на сервере)
- Заголовки — мета-информация об ответе
- Тело — сами данные (чаще всего в формате JSON)
Формат данных: JSON
Большинство современных API используют JSON (JavaScript Object Notation). Выглядит он так:
{
"name": "Иван",
"age": 25,
"hobbies": ["программирование", "шахматы"],
"address": {
"city": "Москва",
"street": "Тверская"
}
}
JSON очень похож на объекты JavaScript, поэтому в Node.js с ним работать особенно удобно — можно превратить JSON в объект одной командой.
Асинхронность
Самое важное, что нужно понять: сетевые запросы — асинхронные. Когда твоя программа отправляет запрос, она не "засыпает" в ожидании ответа. Вместо этого она продолжает работать дальше, а когда ответ приходит — специальная функция (колбэк) его обрабатывает. В современном JavaScript для этого используют async/await, что делает код похожим на обычный синхронный.
⚠️ Критически важно: Если ты попробуешь сделать HTTP-запрос без асинхронности, программа либо зависнет, либо вернёт undefined. Это одна из самых частых ошибок новичков.
Синтаксис в JavaScript (Node.js): все формы и варианты
В Node.js есть несколько способов делать HTTP-запросы. Давай рассмотрим все основные.
1. Встроенный модуль http/https (низкоуровневый)
Node.js имеет встроенные модули http и https. Они не требуют установки, но код получается громоздким. Используются редко, в основном для написания собственных библиотек.
2. Библиотека axios (самая популярная)
Это стандарт де-факто для HTTP-запросов в JavaScript. Удобная, простая, поддерживает промисы и async/await.
3. Библиотека node-fetch (современный стандарт)
Реализация браузерного fetch для Node.js. Легковесная, современная, но требует установки.
4. Библиотека got (мощная и быстрая)
Более новая библиотека с большим количеством возможностей "из коробки".
Сравнение популярных библиотек
| Библиотека | Установка | Популярность | Сложность | Особенности |
|---|---|---|---|---|
| axios | npm install axios |
⭐⭐⭐⭐⭐ | Низкая | Автоматический JSON, перехватчики |
| node-fetch | npm install node-fetch |
⭐⭐⭐⭐ | Низкая | Как в браузере, легковесный |
| got | npm install got |
⭐⭐⭐ | Средняя | Мощный, много опций |
| http/https | Встроенный | ⭐⭐ | Высокая | Не требует установки |
Основные методы HTTP-запросов
| Метод | Что делает | Есть ли тело | Пример использования |
|---|---|---|---|
| GET | Получить данные | Нет | Получить список пользователей |
| POST | Создать новый ресурс | Да | Зарегистрировать нового пользователя |
| PUT | Полностью обновить ресурс | Да | Обновить профиль пользователя |
| PATCH | Частично обновить ресурс | Да | Изменить только email пользователя |
| DELETE | Удалить ресурс | Нет | Удалить пользователя |
Примеры кода — от простого к сложному
Теперь перейдём к самому интересному — практике. Все примеры используют библиотеку axios, так как она самая популярная и простая. Установи её командой npm install axios.
Пример 1: Простой GET-запрос (получаем данные)
// Подключаем библиотеку axios
const axios = require('axios');
// Асинхронная функция для выполнения запроса
async function getUsers() {
try {
// Отправляем GET-запрос на тестовый API
// JSONPlaceholder — это бесплатный сервис с тестовыми данными
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
// response.data — это тело ответа, уже преобразованное в объект JavaScript
console.log('Полученные данные:', response.data);
// response.status — код ответа (200, 404 и т.д.)
console.log('Статус ответа:', response.status);
// response.headers — заголовки ответа
console.log('Заголовки:', response.headers);
} catch (error) {
// Если произошла ошибка (нет интернета, сервер не отвечает)
console.error('Ошибка при запросе:', error.message);
}
}
// Вызываем функцию
getUsers();
Пример 2: POST-запрос (отправляем данные)
const axios = require('axios');
async function createUser() {
try {
// Создаём объект с данными нового пользователя
const newUser = {
name: 'Мария Петрова',
username: 'maria_p',
email: 'maria@example.com'
};
// Отправляем POST-запрос с телом запроса (данными)
const response = await axios.post(
'https://jsonplaceholder.typicode.com/users',
newUser // Второй аргумент — тело запроса
);
// Сервер вернёт созданный объект с присвоенным ID
console.log('Созданный пользователь:', response.data);
console.log('Статус:', response.status); // Должен быть 201 (Created)
} catch (error) {
console.error('Ошибка создания:', error.message);
}
}
createUser();
Пример 3: Обработка ошибок и разных статус-кодов
const axios = require('axios');
async function fetchWithErrorHandling() {
try {
// Пробуем получить несуществующий ресурс (должен вернуть 404)
const response = await axios.get('https://jsonplaceholder.typicode.com/nonexistent');
console.log('Успех:', response.data);
} catch (error) {
// Проверяем, есть ли ответ от сервера (статус-код)
if (error.response) {
// Сервер ответил, но с ошибкой
console.log('Статус ошибки:', error.response.status); // 404
console.log('Сообщение сервера:', error.response.data);
} else if (error.request) {
// Запрос был отправлен, но ответа нет (нет интернета)
console.log('Сервер не отвечает. Проверьте интернет.');
} else {
// Ошибка на этапе настройки запроса
console.log('Ошибка в запросе:', error.message);
}
}
}
fetchWithErrorHandling();
Пример 4: Работа с заголовками (авторизация)
const axios = require('axios');
async function getWithAuth() {
try {
// Создаём конфигурацию запроса
const config = {
headers: {
// Заголовок авторизации (обычно токен)
'Authorization': 'Bearer my-secret-token-123',
// Указываем, что ожидаем JSON
'Accept': 'application/json',
// Указываем, что отправляем JSON
'Content-Type': 'application/json'
},
// Параметры запроса (добавляются к URL)
params: {
limit: 10, // Получить только 10 записей
page: 1 // Первая страница
}
};
const response = await axios.get(
'https://jsonplaceholder.typicode.com/posts',
config // Передаём конфигурацию третьим аргументом
);
console.log('Посты:', response.data);
console.log('Количество:', response.data.length);
} catch (error) {
console.error('Ошибка авторизации:', error.message);
}
}
getWithAuth();
Пример 5: Параллельные запросы (Promise.all)
const axios = require('axios');
async function getMultipleResources() {
try {
// Создаём массив промисов (запросов)
const requests = [
axios.get('https://jsonplaceholder.typicode.com/posts'),
axios.get('https://jsonplaceholder.typicode.com/users'),
axios.get('https://jsonplaceholder.typicode.com/comments')
];
// Ждём выполнения ВСЕХ запросов одновременно
// Это намного быстрее, чем делать их по очереди
const [posts, users, comments] = await Promise.all(requests);
console.log('Постов:', posts.data.length); // 100
console.log('Пользователей:', users.data.length); // 10
console.log('Комментариев:', comments.data.length); // 500
// Теперь можно объединить данные
console.log('Первый пост написал:', users.data[0].name);
} catch (error) {
// Если хотя бы один запрос упадёт — упадут все
console.error('Ошибка в одном из запросов:', error.message);
}
}
getMultipleResources();
Пример 6: Создание своего экземпляра axios (instance)
const axios = require('axios');
// Создаём свой экземпляр с базовыми настройками
// Это удобно, когда работаешь с одним API много раз
const apiClient = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 5000, // Таймаут 5 секунд
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
// Теперь можно использовать apiClient как обычный axios
// Но URL будут относительными
async function workWithApi() {
try {
// Этот запрос пойдёт на https://jsonplaceholder.typicode.com/users
const users = await apiClient.get('/users');
console.log('Пользователи:', users.data);
// Этот запрос пойдёт на https://jsonplaceholder.typicode.com/posts
const posts = await apiClient.get('/posts');
console.log('Посты:', posts.data);
// POST-запрос на https://jsonplaceholder.typicode.com/posts
const newPost = await apiClient.post('/posts', {
title: 'Мой новый пост',
body: 'Текст поста...',
userId: 1
});
console.log('Создан:', newPost.data);
} catch (error) {
console.error('Ошибка:', error.message);
}
}
workWithApi();
Пример 7: Перехватчики (interceptors) — продвинутая техника
const axios = require('axios');
// Создаём экземпляр
const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com'
});
// Перехватчик запроса — выполняется перед каждым запросом
api.interceptors.request.use(
(config) => {
// Можно добавить логирование
console.log(`[${new Date().toISOString()}] ${config.method.toUpperCase()} ${config.url}`);
// Можно автоматически добавить токен авторизации
config.headers.Authorization = 'Bearer ' + getTokenFromStorage();
// Обязательно вернуть config, иначе запрос не уйдёт
return config;
},
(error) => {
// Ошибка при настройке запроса
return Promise.reject(error);
}
);
// Перехватчик ответа — выполняется после каждого ответа
api.interceptors.response.use(
(response) => {
// Можно обработать данные перед тем, как они попадут в код
console.log(`Ответ получен: ${response.status}`);
return response;
},
(error) => {
// Можно обработать ошибки глобально
if (error.response && error.response.status === 401) {
// Если получили 401 (не авторизован) — перенаправить на страницу входа
console.log('Требуется авторизация!');
redirectToLogin();
}
return Promise.reject(error);
}
);
// Вспомогательные функции (для примера)
function getTokenFromStorage() {
return 'fake-token-123';
}
function redirectToLogin() {
console.log('Перенаправление на страницу входа...');
}
// Теперь все запросы через api будут проходить через перехватчики
async function testInterceptors() {
const response = await api.get('/users');
console.log('Данные:', response.data);
}
testInterceptors();
Пример 8: Загрузка файла (stream)
const axios = require('axios');
const fs = require('fs'); // Модуль для работы с файлами
async function downloadFile() {
try {
// Создаём поток записи в файл
const writer = fs.createWriteStream('image.jpg');
// Скачиваем файл с сервера
const response = await axios({
method: 'GET',
url: 'https://example.com/image.jpg',
responseType: 'stream' // Важно! Получаем поток, а не строку
});
// Направляем поток данных из ответа в файл
response.data.pipe(writer);
// Ждём, пока файл запишется полностью
await new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
console.log('Файл успешно скачан!');
} catch (error) {
console.error('Ошибка скачивания:', error.message);
}
}
downloadFile();
Сравнение с Python: чем похоже и чем отличается
Если ты уже знаком с Python или планируешь его изучать, полезно знать, как работа с HTTP API выглядит на обоих языках.
Сходства
- Асинхронность: В обоих языках сетевые запросы асинхронные. В Python используется
asyncioиaiohttp, в JavaScript —async/await. - Библиотеки: В Python самая популярная библиотека —
requests(синхронная) иaiohttp(асинхронная). В JavaScript —axiosиnode-fetch. - JSON: Оба языка отлично работают с JSON. В Python есть
json.loads()иjson.dumps(), в JavaScript —JSON.parse()иJSON.stringify(). - Статус-коды: Обработка статус-кодов (200, 404, 500) идентична.
Различия
| Аспект | JavaScript (Node.js) | Python |
|---|---|---|
| Основная библиотека | axios (асинхронная) | requests (синхронная) |
| Асинхронность | По умолчанию асинхронный | По умолчанию синхронный (нужен aiohttp для асинхронности) |
| JSON автоматически | axios сам парсит JSON | requests сам парсит JSON |
| Синтаксис | await axios.get(url) |
requests.get(url) |
| Обработка ошибок | try/catch с axios | try/catch с requests |
| Типизация | Динамическая, нет типов | Динамическая, но можно добавить типы |
Пример на Python для сравнения
// JavaScript (Node.js)
const axios = require('axios');
async function getData() {
const response = await axios.get('https://api.example.com/data');
console.log(response.data);
}
# Python (с библиотекой requests)
import requests
def get_data():
response = requests.get('https://api.example.com/data')
print(response.json())
💡 Совет: Если ты знаешь Python, переход на JavaScript будет лёгким. Главное отличие — не забывай проawaitиasyncв JavaScript. В Python ты можешь писать синхронный код, а в Node.js — нет.
Типичные ошибки и подводные камни
Даже опытные разработчики иногда допускают эти ошибки. Вот список самых частых проблем и как их избежать.
1. Забыл про асинхронность
// ❌ НЕПРАВИЛЬНО: функция не async, нет await
function getUsers() {
const response = axios.get('https://api.example.com/users');
console.log(response.data); // undefined — запрос ещё не выполнен!
}
// ✅ ПРАВИЛЬНО: используем async/await
async function getUsers() {
const response = await axios.get('https://api.example.com/users');
console.log(response.data); // Теперь данные есть
}
2. Не обрабатываешь ошибки
// ❌ НЕПРАВИЛЬНО: нет try/catch
async function getData() {
const response = await axios.get('https://nonexistent-url.com');
// Если сервер не отвечает — программа упадёт с ошибкой
}
// ✅ ПРАВИЛЬНО: всегда оборачивай в try/catch
async function getData() {
try {
const response = await axios.get('https://nonexistent-url.com');
console.log(response.data);
} catch (error) {
console.error('Ошибка:', error.message);
}
}
3. Путаешь методы GET и POST
// ❌ НЕПРАВИЛЬНО: пытаешься отправить данные через GET
await axios.get('/users', { name: 'Иван' }); // Данные не отправятся!
// ✅ ПРАВИЛЬНО: для отправки данных используй POST
await axios.post('/users', { name: 'Иван' });
4. Забываешь про заголовки
// ❌ НЕПРАВИЛЬНО: не указал Content-Type
await axios.post('/api/data', jsonData);
// Сервер может не понять, что ты отправляешь JSON
// ✅ ПРАВИЛЬНО: всегда указывай заголовки
await axios.post('/api/data', jsonData, {
headers: {
'Content-Type': 'application/json'
}
});
5. Игнорируешь таймауты
// ❌ НЕПРАВИЛЬНО: без таймаута программа может зависнуть навсегда
const response = await axios.get('https://slow-server.com');
// ✅ ПРАВИЛЬНО: устанавливай разумный таймаут
const response = await axios.get('https://slow-server.com', {
timeout: 5000 // 5 секунд — и ошибка
});
6. Не проверяешь статус-код
// ❌ НЕПРАВИЛЬНО: доверяешь, что всё всегда 200
const response = await axios.get('/api/data');
// Если сервер вернул 404 или 500 — данные могут быть пустыми
// ✅ ПРАВИЛЬНО: проверяй статус
const response = await axios.get('/api/data');
if (response.status === 200) {
console.log('Данные:', response.data);
} else {
console.log('Неожиданный статус:', response.status);
}
7. Делаешь запросы в цикле последовательно
// ❌ НЕПРАВИЛЬНО: медленно — каждый запрос ждёт предыдущий
for (const id of [1, 2, 3, 4, 5]) {
const response = await axios.get(`/items/${id}`);
console.log(response.data);
}
// ✅ ПРАВИЛЬНО: используй Promise.all для параллельных запросов
const requests = [1, 2, 3, 4, 5].map(id => axios.get(`/items/${id}`));
const responses = await Promise.all(requests);
responses.forEach(r => console.log(r.data));
Лучшие практики и советы профессионалов
Эти советы помогут тебе писать не просто работающий, а качественный и надёжный код.
1. Всегда используй try/catch
Сетевые запросы — это самое ненадёжное место в программе. Интернет может пропасть, сервер может упасть, API может измениться. Оборачивай каждый запрос в try/catch.
2. Создавай отдельный модуль для API
Не разбрасывай запросы по всему коду. Создай один файл (например, api.js), где будут все функции для работы с внешними сервисами.
// api.js — отдельный модуль для работы с API
const axios = require('axios');
const client = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
module.exports = {
getUsers: () => client.get('/users'),
getUserById: (id) => client.get(`/users/${id}`),
createUser: (data) => client.post('/users', data),
updateUser: (id, data) => client.put(`/users/${id}`, data),
deleteUser: (id) => client.delete(`/users/${id}`)
};
3. Используй переменные окружения для секретов
Никогда не храни API-ключи и токены в коде. Используй файл .env и библиотеку dotenv.
// .env файл
API_KEY=my-secret-key-123
API_URL=https://api.example.com
// В коде
require('dotenv').config();
const apiKey = process.env.API_KEY;
const apiUrl = process.env.API_URL;
4. Логируй запросы и ответы
Когда что-то идёт не так, логи помогут быстро найти проблему. Используй перехватчики (interceptors) для автоматического логирования.
5. Ограничивай количество запросов (rate limiting)
Многие API ограничивают количество запросов в минуту. Уважай эти ограничения — используй задержки между запросами.
// Функция для задержки
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Делаем запросы с задержкой в 1 секунду
for (let i = 0; i < 10; i++) {
await axios.get('/api/data');
await delay(1000); // Ждём 1 секунду
}
6. Используй кэширование
Если данные меняются редко, не запрашивай их каждый раз. Сохраняй результат в переменную или файл.
let cachedUsers = null;
let cacheTime = null;
async function getUsers() {
// Если данные были получены меньше минуты назад — возвращаем кэш
if (cachedUsers && Date.now() - cacheTime < 60000) {
return cachedUsers;
}
const response = await axios.get('/users');
cachedUsers = response.data;
cacheTime = Date.now();
return cachedUsers;
}
7. Обрабатывай частичные ошибки
Если ты делаешь 10 запросов и один упал — не обязательно ронять всю программу. Используй Promise.allSettled вместо Promise.all.
const results = await Promise.allSettled(requests);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Запрос ${index} успешен:`, result.value.data);
} else {
console.log(`Запрос ${index} упал:`, result.reason.message);
}
});
Вопросы для самопроверки
Проверь, насколько хорошо ты усвоил материал. Попробуй ответить на вопросы, не подглядывая в ответы.
-
Вопрос: Что такое HTTP API простыми словами?
Ответ
Это набор правил, по которым одна программа может общаться с другой через интернет. Как официант в