Требования к HTTP-запросам
Цель
Слой API отвечает исключительно за HTTP-запросы и транспорт.
Он не управляет состоянием и не знает о UI.
Общие принципы
MUST
- Вся логика сетевого взаимодействия находится исключительно в слое API:
src/shared/api/*. - В проекте обязан быть единый HTTP-клиент (singleton / factory), который:
- Выполняет HTTP-запросы
- Централизованно конфигурируется (
baseURL,timeout,headers) - Использует middleware/interceptors
- Перехватывает все неуспешные запросы (4xx/5xx)
- Выполняет логирование сетевых ошибок
- Поддерживает отмену запросов (
AbortController)
- Все запросы строго типизированы.
- Контрак т API и типизация:
- Все запросы строго типизированы
- Допустимые источники типов:
- Сгенерированные DTO (OpenAPI)
- Вручную описанные DTO (
shared/api/dtoилиpackages/api/dto)ƒ
SHOULD
- API слой предоставляет тонкие сервисы по доменам (например
usersApi,ordersApi) без бизнес-логики. - Ошибки приводятся к единому формату (
ApiError) для удобной обработки выше по стеку. - Принимать
AbortSignalво всех API методах:- Tanstack Query предоставляет автоматическую отмену, достаточно встроенного механизма
- API методы просто пробрасывают
signalв HTTP-клиент
FORBIDDEN
- Использование HTTP-клиента вне
shared/api - Реализация бизнес-логики внутри
shared/api - Импорт UI-зависимостей в API слое (toast, router, store, components)
- Использование
any,unknown,object,{}в типах DTO - Inline-типы в сигнатурах запросов
Допустимые библиотеки HTTP-клиента
MUST
В проекте должна быть выбрана одна библиотека и один единый клиент.
Допустимые библиотеки
Стиль экспорта API методов
Функцион альный стиль (именованные экспорты) разрешён и часто удобнее в больших проектах, если соблюдать правила слоя shared/api.
✅ Хорошо (functions + строгая типизация + DTO)
import type { UserDto, UpdateUserDto } from '@/shared/api/dto';
export function getUserById(id: string, signal?: AbortSignal) {
return http.get<UserDto>(`/users/${id}`, { signal });
}
export function updateUser(
id: string,
dto: UpdateUserDto,
signal?: AbortSignal
) {
return http.put<UserDto>(`/users/${id}`, { json: dto, signal });
}
Почему это хорошо:
- Все типы вынесены в DTO (
UserDto,UpdateUserDto) - Нет
any/unknown/object/{}в DTO - Есть поддержка отмены (
AbortSignal) - Нет бизнес-логики, только транспортный вызов