При разработке веб-приложений фронтенд и серверная часть нередко работают на разных адресах. Например:
http://localhost:1234;http://localhost:3000.Попытка выполнить запрос из браузера к другому источнику может привести к ограничениям политики безопасности браузера (Same-Origin Policy) и возникновению ошибок CORS.
Проксирование API-запросов позволяет перенаправлять обращения клиента через сервер разработки Parcel. В результате браузер взаимодействует только с одним источником, а Parcel незаметно передаёт запросы нужному API-серверу.
Схема работы выглядит следующим образом:
Браузер
│
▼
Parcel Dev Server
│
▼
API-сервер
Такой подход упрощает разработку и избавляет от необходимости изменять настройки CORS на сервере.
Предположим, приложение выполняет следующий запрос:
fetch('http://localhost:3000/api/users')
.then(response => response.json())
.then(data => console.log(data));
Если страница открыта по адресу:
http://localhost:1234
то браузер воспринимает запрос как междоменный, поскольку различаются порты.
Даже при одинаковом домене:
localhost:1234
localhost:3000
это уже два разных источника.
При отсутствии соответствующих CORS-заголовков сервер может вернуть ошибку:
Access to fetch at 'http://localhost:3000/api/users'
from origin 'http://localhost:1234'
has been blocked by CORS policy
Прокси решает эту проблему путём промежуточной обработки запросов.
Вместо прямого обращения к API:
fetch('http://localhost:3000/api/users')
используется относительный путь:
fetch('/api/users')
Для браузера запрос отправляется на тот же источник:
http://localhost:1234/api/users
Далее Parcel принимает запрос и перенаправляет его на настоящий сервер:
http://localhost:3000/api/users
Для клиентского кода различия отсутствуют.
Parcel позволяет задавать прокси через поле proxy.
Пример:
{
"name": "parcel-app",
"proxy": "http://localhost:3000"
}
После запуска сервера разработки:
parcel serve src/index.html
все неизвестные маршруты будут автоматически перенаправляться на указанный сервер.
Теперь запрос:
fetch('/api/users')
будет преобразован в:
http://localhost:3000/api/users
project/
│
├── src/
│ ├── index.html
│ ├── app.js
│ └── styles.css
│
├── package.json
└── .parcelrc
Файл package.json:
{
"name": "parcel-demo",
"version": "1.0.0",
"proxy": "http://localhost:3000"
}
Файл app.js:
async function loadUsers() {
const response = await fetch('/api/users');
const users = await response.json();
console.log(users);
}
loadUsers();
В результате приложение не зависит от конкретного адреса сервера.
Прокси одинаково работает для любых HTTP-методов.
fetch('/api/products');
После перенаправления:
GET http://localhost:3000/api/products
fetch('/api/products', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'Laptop'
})
});
Прокси передаст:
POST http://localhost:3000/api/products
вместе со всеми заголовками и телом запроса.
fetch('/api/products/10', {
method: 'PUT',
body: JSON.stringify({
title: 'Updated laptop'
})
});
fetch('/api/products/10', {
method: 'DELETE'
});
Все методы работают одинаковым образом.
Прокси не зависит от используемой HTTP-библиотеки.
Пример с Axios:
import axios from 'axios';
async function getPosts() {
const response = await axios.get('/api/posts');
console.log(response.data);
}
getPosts();
Parcel перенаправит запрос на API-сервер точно так же, как и в случае с Fetch API.
GraphQL-серверы также часто располагаются отдельно от клиентского приложения.
Запрос:
fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: `
query {
users {
id
name
}
}
`
})
});
После обработки прокси:
POST http://localhost:3000/graphql
Для клиента взаимодействие остаётся прозрачным.
Прокси особенно полезен при использовании авторизации на основе cookies.
Пример:
fetch('/api/profile', {
credentials: 'include'
});
Браузер считает запрос локальным:
http://localhost:1234/api/profile
Поэтому работа с cookie обычно становится значительно проще по сравнению с прямыми междоменными запросами.
Часто адрес API различается для разных окружений.
Файл .env:
API_URL=http://localhost:3000
Клиентский код:
console.log(process.env.API_URL);
Однако при использовании прокси фронтенд может вообще не знать реальный адрес сервера:
fetch('/api/users');
Такой подход уменьшает количество изменений между средами разработки и продакшена.
| Характеристика | Прямой запрос | Через прокси |
|---|---|---|
| Возможны CORS-ошибки | Да | Нет |
| Требуется настройка сервера | Часто | Обычно нет |
| Используются абсолютные URL | Да | Нет |
| Удобство локальной разработки | Среднее | Высокое |
| Единая точка доступа | Нет | Да |
Для проверки работы прокси удобно использовать вкладку Network в инструментах разработчика браузера.
Клиентский запрос:
fetch('/api/users');
В панели разработчика будет отображаться:
http://localhost:1234/api/users
Хотя фактически Parcel перенаправляет запрос на внешний сервер.
При возникновении ошибок необходимо проверить:
Даже при корректной настройке прокси сервер может быть недоступен.
Пример безопасной обработки:
async function loadData() {
try {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Ошибка запроса:', error);
}
}
Такой подход позволяет корректно реагировать на:
Предположим, имеются два независимых проекта.
Сервер:
http://localhost:5000
Клиент на Parcel:
http://localhost:1234
Настройка:
{
"proxy": "http://localhost:5000"
}
Клиентский код:
const response = await fetch('/api/orders');
Последовательность действий:
1. Браузер отправляет запрос на localhost:1234.
2. Parcel принимает запрос.
3. Parcel пересылает его на localhost:5000.
4. Сервер формирует ответ.
5. Parcel возвращает результат браузеру.
Клиентское приложение при этом остаётся полностью независимым от фактического расположения серверной части.
Использование относительных URL
Предпочтительно:
fetch('/api/users');
Вместо:
fetch('http://localhost:3000/api/users');
Единый префикс API
Удобно группировать все серверные маршруты:
/api/users
/api/posts
/api/orders
Это облегчает настройку прокси и сопровождение проекта.
Изоляция конфигурации
Адреса серверов должны храниться в конфигурационных файлах, а не в исходном коде.
Проверка сетевых запросов
Вкладка Network позволяет быстро определить, возникла проблема в клиентском коде, в прокси или на стороне сервера.
Разделение среды разработки и продакшена
Прокси используется главным образом во время локальной разработки. В производственной среде обычно применяется обратный прокси на базе веб-сервера, например Nginx или Apache HTTP Server, который выполняет аналогичную задачу уже на уровне инфраструктуры.