раздел 05 · шаг 4/7
Шаг 4. Связка фронт-бэк
Базовая интеграция работает, но у нас два косяка: TS-типы написаны руками (расходятся с бэком), и нет состояний loading/error/empty. Чиним.
Что делаем
- Генерим TypeScript-типы из FastAPI OpenAPI-схемы
- Подключаем сгенерированные типы в фронт, удаляем ручные
- Добавляем loading/error/empty состояния в UI
- Проверяем что всё типизировано end-to-end
Шаг 4.1. Генерация типов
FastAPI отдаёт OpenAPI-схему по адресу http://localhost:8000/openapi.json. По ней можно сгенерировать готовые TS-типы через openapi-typescript.
Промпт для Claude
Настрой генерацию TypeScript-типов из FastAPI OpenAPI-схемы:
1. Установи в frontend/ devDependency: openapi-typescript
2. Добавь в frontend/package.json скрипт:
"types:gen": "openapi-typescript http://localhost:8000/openapi.json -o src/types/api.ts"
3. Сгенерируй типы (бэк должен быть запущен)
4. Удали ручной src/types.ts
5. Замени все импорты Task на сгенерированные типы из src/types/api.ts
(там будет components["schemas"]["TaskRead"] и т.п.)
6. Сделай удобные алиасы в src/types/index.ts:
export type Task = components["schemas"]["TaskRead"]
export type TaskCreate = components["schemas"]["TaskCreate"]
export type TaskUpdate = components["schemas"]["TaskUpdate"]
7. Обнови fetchApi в src/lib/api.ts: типизируй на основе сгенерированных типов
Запуск
Бэк должен быть запущен. В отдельном терминале:
cd frontend
pnpm install
pnpm types:gen
В frontend/src/types/api.ts появится сгенерированный файл - его в git коммитим, переписывать руками не нужно.
Шаг 4.2. Состояния UI
Промпт для Claude
Добавь loading, error и empty состояния в TaskList:
- loading: пока useQuery в isPending, показывай 3 skeleton-строки
(фон zinc-900, скругление rounded-lg, animate-pulse)
- error: если isError, показывай красную плашку с текстом ошибки и кнопкой "Повторить",
которая вызывает refetch
- empty: если data.length === 0, показывай центрированный текст
"Задач пока нет. Добавьте первую сверху." с приглушённым цветом text-zinc-500
Аналогично для мутаций:
- POST /api/tasks: пока в isPending - инпут disabled, плейсхолдер "Добавляем..."
- PATCH: пока isPending - чекбокс показывает спиннер вместо галочки
- DELETE: пока isPending - строка с opacity-50
Используй типы из src/types вместо any.
Что должно получиться
После этого шага UI ведёт себя адекватно:
- Открыли страницу - сразу видно скелетон, потом список
- Бэк упал - красная плашка, можно перезапросить
- Нет задач - понятный текст приглашения
- Создание/удаление/обновление - визуальная обратная связь
Проверка типизации
cd frontend
pnpm tsc --noEmit
Должно пройти без ошибок. Если TS ругается - значит, где-то ручной тип остался. Найти и заменить на сгенерированный.
Тест end-to-end
- Бэк запущен (
uvicorn) - Фронт запущен (
pnpm dev) - Открываете
http://localhost:5173 - Видите skeleton, потом список (или empty)
- Добавляете задачу - появляется
- Чекаете - сохраняется (PATCH улетел)
- Удаляете - исчезает (DELETE улетел)
- Останавливаете бэк, перезагружаете фронт - видите error-плашку
- Запускаете бэк обратно, нажимаете "Повторить" - данные подтягиваются
Возможные проблемы
- openapi-typescript не видит бэк - проверьте что
uvicornзапущен,curl http://localhost:8000/openapi.jsonдолжен вернуть JSON - TS-ошибки про
nullв типах - в Pydantic v2Optional[X]превращается вX | null, а неX | undefined. Поправьте в TS обработку соответственно. - TanStack Query не обновляет список после POST - проверьте что в мутации вызывается
queryClient.invalidateQueries({ queryKey: ['tasks'] })
Точка сохранения
cd ..
git add .
git commit -m "step 4: openapi-typescript types, loading/error/empty states"
Полезные ссылки
- openapi-typescript - генерация типов из OpenAPI
- TanStack Query: queries - паттерны isPending/isError
- FastAPI OpenAPI - как настраивать схему