Skip to content

React frontend на esbuild

В корневой папке frontend находится рабочий consumer-проект:

  • esbuild;
  • TypeScript strict;
  • React;
  • MobX;
  • mobx-react-lite;
  • Axios;
  • Valibot;
  • локальный @dubium/query-layer из соседней папки package.

Текущий consumer проверяется с MobX 7.0.3 и mobx-react-lite 5.0.3. mobx-react-lite 5 требует React 18+, а этот workspace использует React 19.

Запуск

Откройте два терминала из корня проекта.

Терминал 1:

bash
npm run backend:dev

Терминал 2:

bash
npm run frontend:dev

Откройте http://127.0.0.1:3200. Frontend обращается к Express API http://127.0.0.1:3100/api.

frontend:dev и frontend:lint сначала запускают lifecycle scripts predev и prelint, которые собирают соседний workspace @dubium/query-layer. После этого esbuild и TypeScript резолвят импорт пакета через собранный package/dist. Поэтому для первого запуска и CI lint не требуется вручную создавать dist, а workspace symlink из node_modules не может сослаться на ещё не собранную точку входа.

Ожидаемая строка после запуска:

text
Query Layer frontend: http://127.0.0.1:3200

Не помечайте @dubium/query-layer как external: браузер не сможет разрешить такой bare import без отдельной import map.

Проверки

bash
npm run frontend:typecheck
npm run frontend:build

Общая команда workspace также включает frontend:

bash
npm run typecheck
npm run build

Структура

text
frontend/
├── esbuild.config.mjs
├── index.html
├── package.json
├── tsconfig.json
└── src/
    ├── app/App.tsx
    ├── data/
    │   ├── api.ts
    │   ├── token/
    │   ├── users/
    │   ├── profile/
    │   └── diagnostics/
    ├── shared/config.ts
    ├── main.tsx
    └── styles.css

Проверяемые сценарии

Список и создание пользователей

UsersStore композиционно использует два handler:

  • createQueryStore автоматически загружает список;
  • createRequestStore выполняет команду создания;
  • после успешной команды business store вызывает refetch списка;
  • React читает только users, loading, creating, status и error.

Private profile и refresh

ProfileStore использует FetchStore. Первый request отправляет просроченный access-v1 и получает 401. authApi выполняет refresh, сохраняет access-v2 и один раз повторяет profile request. UI получает только итоговое MobX-состояние. В этом demo внешняя обёртка сохраняет предметное поле profile и единый lifecycle feature. Для простого GET без такой логики можно экспортировать настроенный FetchStore напрямую, как описано в странице FetchStore.

Отмена

SlowRequestStore использует createRequestStore. Метод cancel() отменяет Axios request через автоматически добавленный AbortSignal. Express backend фиксирует закрытие соединения.

API-функции не импортируются React-компонентом

App.tsx импортирует:

ts
import { usersStore } from "../data/users"
import { profileStore } from "../data/profile"
import { slowRequestStore } from "../data/diagnostics"

Он не импортирует:

ts
import { publicApi, authApi } from "../data/api"
import { getUsersApi } from "../data/users/api/users.api"

Это и есть проверяемая архитектурная граница проекта.

CORS

Backend разрешает local origins:

Preflight для JSON и Authorization headers проверяется backend-тестом.