Взаимодействие клиентской части (фронтенда) с серверной (бэкендом) — это фундамент современной веб-разработки․ Процесс отправки запроса с сайта на сервер включает в себя несколько этапов: формирование данных, выбор протокола и метода, обработка ответа и работа с ошибками․ В этой статье мы подробно разберем, как это работает на стороне браузера (клиент) и на стороне сервера (Node․js), опираясь на современные стандарты и популярные инструменты․
Основы HTTP-взаимодействия
Прежде чем писать код, важно понимать базовые понятия․ Обмен данными в вебе происходит по протоколу HTTP/HTTPS․ Каждый запрос состоит из:
- Метода (глагола): определяет действие (GET, POST, PUT, PATCH, DELETE)․
- URL (эндпоинта): адрес ресурса на сервере․
- Заголовков (Headers): метаданные (тип контента, авторизация, кэширование)․
- Тела (Body): полезная нагрузка (обычно JSON, FormData или бинарные данные), передается при POST/PUT/PATCH․
Основные HTTP-методы
| Метод | Назначение | Тело запроса | Идемпотентность |
|---|---|---|---|
| GET | Получение данных | Нет | Да |
| POST | Создание нового ресурса | Да | Нет |
| PUT | Полное обновление ресурса | Да | Да |
| PATCH | Частичное обновление ресурса | Да | Нет |
| DELETE | Удаление ресурса | Редко | Да |
Часть 1: Клиентская сторона (Браузер / Frontend)
В браузере есть два основных API для выполнения сетевых запросов: старый XMLHttpRequest (устарел, но встречается в легаси) и современный Fetch API․ Также широко используются сторонние библиотеки, такие как Axios․
Fetch API (Стандарт браузера)
fetch — это глобальная функция, возвращающая Promise․ Она доступна во всех современных браузерах без установки пакетов․
Базовый GET-запрос
async function loadUsers {
try {
const response = await fetch('https://jsonplaceholder․typicode․com/users');
// fetch не выбрасывает ошибку при HTTP-статусах 4xx/5xx, нужно проверять response․ok
if (!response․ok) {
throw new Error(`Ошибка HTTP: ${response․status}`);
}
const users = await response․json;
console․log(users);
} catch (error) {
console․error('Ошибка загрузки:', error);
}
}
loadUsers; POST-запрос с отправкой JSON
async function createUser(userData) {
try {
const response = await fetch('https://jsonplaceholder․typicode․com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // Важно указывать тип контента
},
body: JSON․stringify(userData), // Превращаем объект в JSON-строку
});
if (!response․ok) throw new Error('Не удалось создать пользователя');
const newUser = await response․json;
console․log('Создан:', newUser);
} catch (error) {
console․error(error);
}
}
createUser({ name: 'Иван', email: 'ivan@example․com' }); Отправка формы (FormData)
Если сервер ожидает multipart/form-data (например, загрузка файлов), не нужно устанавливать заголовок Content-Type вручную — браузер сделает это сам с правильной границей (boundary)․
const form = document․getElementById('myForm');
form․addEventListener('submit', async (e) => {
e․preventDefault;
const formData = new FormData(form); // Автоматически собирает данные из input
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const result = await response․json;
alert('Файл загружен!');
}); Axios (Популярная библиотека)
Axios устанавливается через npm/yarn/pnpm: npm install axios․ Он работает и в браузере, и в Node․js․
Преимущества Axios:
- Автоматическое преобразование JSON (не нужно вызывать
․json)․ - Обработка ошибок: выбрасывает исключение при статусах 4xx/5xx․
- Поддержка интерцепторов (перехватчиков) для добавления токенов авторизации․
- Защита от XSRF на клиенте․
- Удобная отмена запросов (AbortController / CancelToken)․
Примеры с Axios
import axios from 'axios';
// Создаем экземпляр с базовым URL (Best Practice)
const api = axios․create({
baseURL: 'https://api․example․com',
timeout: 5000, // Таймаут 5 секунд
headers: { 'X-Custom-Header': 'value' }
});
// Интерцептор для добавления токена к каждому запросу
api․interceptors․request․use(config => {
const token = localStorage․getItem('authToken');
if (token) config․headers․Authorization = `Bearer ${token}`;
return config;
});
// GET
async function getUsers {
try {
const response = await api․get('/users'); // Данные в response․data
console․log(response․data);
} catch (error) {
handleApiError(error);
}
}
// POST
async function createPost(title, body) {
try {
const response = await api;post('/posts', { title, body });
console․log('Создано:', response․data);
} catch (error) {
handleApiError(error);
}}
function handleApiError(error) {
if (error․response) {
// Сервер ответил с кодом ошибки (4xx, 5xx)
console․error('Статус:', error․response․status);
console․error('Данные ошибки:', error․response․data);
} else if (error․request) {
// Запрос ушел, ответа нет (сеть, CORS, таймаут)
console․error('Нет ответа от сервера');
} else {
// Ошибка настройки запроса
console․error('Ошибка:', error․message);
}
} Работа с аутентификацией и Cookies
По умолчанию fetch не отправляет и не принимает cookies (включая HttpOnly) при кросс-доменных запросах․ Для этого нужно явно указать credentials: 'include'․
// Fetch с куками
fetch('https://api․another-domain․com/profile', {
method: 'GET',
credentials: 'include' // Важно для отправки/приема cookies
});
// Axios (настройка withCredentials)
axios․get('https://api․another-domain․com/profile', {
withCredentials: true
}); Важно: Сервер должен ответить заголовком
Access-Control-Allow-Credentials: trueи конкретнымAccess-Control-Allow-Origin(не*), иначе браузер заблокирует ответ (CORS policy)․
Часть 2: Серверная сторона (Node․js / Backend)
Часто возникает необходимость отправить запрос с сервера на другой сервер (микросервисы, интеграция с платежными шлюзами, внешние API)․ Начиная с Node․js 18, Fetch API доступен глобально без установки зависимостей․ Для старых версий (v16, v14) используется пакет node-fetch или нативный модуль https․
Нативный Fetch (Node․js 18+)
Синтаксис идентичен браузерному, но есть нюансы: нет объекта window, нет FormData (до Node 18/20 требовался полифилл), другие ошибки сети․
GET запрос с TypeScript
// Типизация ответа
interface ApiResponse {
id: number;
name: string;
email: string;
}
async function fetchUserData: Promise<ApiResponse | null> {
try {
const response = await fetch('https://jsonplaceholder․typicode․com/users/1');
if (!response․ok) {
// В Node;js fetch не бросает ошибку на 404/500
throw new Error(`HTTP Error: ${response․status} ${response․statusText}`);
}
const data: ApiResponse = await response․json;
return data;
} catch (error) {
console․error('Ошибка при запросе к внешнему API:', error);
return null;
}
} POST запрос с JSON
async function sendNotification(webhookUrl: string, payload: object) {
try {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'MyApp/1․0' // Хорошая практика указывать User-Agent
},
body: JSON․stringify(payload),
// Node․js специфичные опции (не работают в браузере):
// agent: new https․Agent({ keepAlive: true }), // Пулинг соединений
// timeout: 5000 // В нативном fetch пока нет таймаута, нужна AbortController
});
if (!response․ok) {
const errorText = await response․text;
throw new Error(`Webhook failed: ${response․status} ⎻ ${errorText}`);
}
return await response․json;
} catch (error) {
// Обработка сетевых ошибок (ENOTFOUND, ECONNREFUSED, ETIMEDOUT)
console․error('Сетевая ошибка:', error․code || error․message);
throw error; // Пробрасываем выше для ретраев или логирования
}} Axios в Node․js
Axios остается популярным выбором для бэкенда благодаря удобству, встроенным таймаутам и автоматической сериализации․
// npm install axios
import axios from 'axios';
// Создание клиента с базовыми настройками (Best Practice для микросервисов)
const httpClient = axios․create({
baseURL: 'https://api․payment-gateway․com/v1',
timeout: 10000, // 10 секунд таймаут (работает из коробки!)
headers: {
'Authorization': `Bearer ${process․env․PAYMENT_API_KEY}`,
'Idempotency-Key': crypto․randomUUID // Защита от двойных списаний
}
});
// POST запрос
async function chargeCustomer(amount: number, currency: string) {
try {
const response = await httpClient․post('/charges', {
amount,
currency,
source: 'token_from_client'
});
// Axios всегда возвращает данные в response;data
return response․data;
} catch (error) {
if (axios․isAxiosError(error)) {
// Типизированная ошибка Axios
if (error․response) {
// Ответ получен, но статус 4xx/5xx
throw new PaymentError(error․response․data․message, error․response․status);
} else if (error․request) {
// Таймаут или нет связи
throw new NetworkError('Платежный шлюз недоступен');
} } throw error;
}} Нативный модуль https (Без зависимостей, низкий уровень)
Используется, если нельзя установить пакеты (например, в AWS Lambda слоях без node_modules или для максимальной производительности/контроля)․ Работает на колбэках/промисах вручную․
import https from 'https';
function postJson(url: string, data: object): Promise<any> {
return new Promise((resolve, reject) => {
const postData = JSON․stringify(data);
const parsedUrl = new URL(url);
const options = {
hostname: parsedUrl․hostname,
port: parsedUrl․port || 443,
path: parsedUrl․pathname + parsedUrl;search,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer․byteLength(postData)
},
timeout: 5000 // Таймаут на сокете
};
const req = https․request(options, (res) => {
let rawData = '';
res․on('data', (chunk) => { rawData += chunk; });
res․on('end', => {
try {
const parsed = JSON․parse(rawData);
if (res․statusCode && res․statusCode >= 400) {
reject(new Error(`HTTP ${res․statusCode}: ${parsed․message || rawData}`));
} else {
resolve(parsed);
}
} catch (e) {
reject(new Error('Invalid JSON response'));
}
});
});
req․on('error', (e) => reject(e));
req․on('timeout', => { req․destroy; reject(new Error('Request timeout')); });
req․write(postData);
req․end;
});
} Часть 3: Лучшие практики и безопасность
Обработка ошибок и Retry-стратегии
Сетевые запросы могут падать по множеству причин․ На бэкенде критически важно реализовывать повторные попытки (retry) с экспоненциальной задержкой (exponential backoff) для идемпотентных операций (GET, PUT, DELETE)․
// Пример утилиты для ретраев (упрощенно)
async function fetchWithRetry(url, options, retries = 3, delay = 1000) {
try {
return await fetch(url, options);
} catch (error) {
if (retries === 0) throw error;
// Ждем перед следующей попыткой
await new Promise(r => setTimeout(r, delay));
return fetchWithRetry(url, options, retries ⎻ 1, delay * 2);
}
} Таймауты (Timeouts)
- Браузер: Используйте
AbortController․ - Node․js (fetch):
const controller = new AbortController; setTimeout( => controller․abort, 5000); fetch(url, { signal: controller․signal })․ - Axios: Встроенная опция
timeout: 5000․
Безопасность (Security)
- HTTPS Only: Никогда не отправляйте чувствительные данные по HTTP․
- Валидация SSL: В Node․js по умолчанию проверяются сертификаты․ Отключение (
rejectUnauthorized: false) — критическая уязвимость (MITM)․ - Секреты в коде: Не хардкодьте API-ключи․ Используйте переменные окружения (
process․env)․ - CORS: Настраивайте на сервере строго: разрешайте только нужные домены, методы и заголовки․
- Rate Limiting: Защищайте свои эндпоинты от DDoS и брутфорса лимитерами запросов․
Производительность
- Connection Pooling (Keep-Alive): В Node․js используйте
agent: new http․Agent({ keepAlive: true })или настройку Axios для переиспользования TCP-соединений при частых запросах к одному хосту․ - Сжатие: Убедитесь, что клиент отправляет
Accept-Encoding: gzip, deflate, br, а сервер сжимает ответы․ - Кэширование: Используйте заголовки
ETag,If-None-Match,Cache-Controlдля GET-запросов․
Сравнительная таблица: Что выбрать?
| Инструмент | Среда | Плюсы | Минусы | Когда использовать |
|---|---|---|---|---|
| Fetch API | Браузер, Node 18+ | Стандарт, без зависимостей, Promise-based, Streaming | Нет таймаутов «из коробки», не бросает ошибку на 4xx/5xx, verbose для простых задач | Современные проекты без лишних зависимостей, Edge Runtime (Cloudflare Workers, Vercel Edge) |
| Axios | Браузер, Node․js | Интерцепторы, таймауты, авто-JSON, отмена запросов, XSRF защита, широкая поддержка TS | Внешняя зависимость (~50kb gzip), класс-обертка (не стандарт Web API) | Сложные приложения, нужна централизованная обработка ошибок/авторизации, работа с FormData/потоками |
| Node https/http | Только Node․js | Нет зависимостей, полный контроль, максимальная скорость, стриминг | Низкий уровень, ручное управление промисами/буферами, нет редиректов, нет пулинга по умолчанию | Serverless функции с лимитом размера, высоконагруженные прокси, легаси Node < 18 |
| Ky / Got | Браузер / Node․js | Легкие, современные обертки над Fetch, удобный синтаксис, хуки, ретраи встроены | Зависимости (маленькие), менее известны, чем Axios | Альтернатива Axios, если нужен стандарт Fetch, но с удобным API |
Отправка запроса с сайта на сервер — задача, которая решается по-разному в зависимости от контекста:
- На фронтенде (Браузер): Стандартом де-факто является Fetch API для простых задач и Axios для сложных приложений с требованием к интерцепторам и удобной обработке ошибок․
- На бэкенде (Node․js): Начиная с версии 18, нативный Fetch стал полноценным конкурентом библиотекам․ Axios остается отличным выбором за счет встроенных таймаутов и удобства․ Для высоконагруженных систем без зависимостей — нативный модуль
https․
Ключевые принципы надежной интеграции: всегда проверяйте response․ok / статус код, используйте таймауты, логируйте ошибки с контекстом и храните секреты в переменных окружения․ Правильная архитектура сетевого слоя сэкономит часы отладки в продакшене․