Files
frontend/.ai/conventions/data.md
T
ksv741 b75ca72ea7
Frontend verify / commits (pull_request) Successful in 44s
Frontend verify / security (pull_request) Successful in 11s
Frontend verify / verify (pull_request) Successful in 6m44s
build: use native openapi-typescript --check (#60)
2026-10-13 21:24:24 +03:00

100 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend data
## Выбор owner
Server state принадлежит TanStack Query, form state — React Hook Form, локальное
UI — React state. Не копируй успешный query result в useState/context для второго
кэша. Domain-specific queries, keys и policies принадлежат вызывающему slice;
общий transport — `shared/api`. Page VM выбирает inputs и собирает presentation data.
Используй `apiClient`/`apiQueryClient` из shared public API. Прямой fetch в
pages/features/entities и прямой T-Bank call запрещены. Сначала найди
существующий query того же endpoint; расширяй его owner, не создавай второй key
или глобальную query policy для одной страницы.
## Типы и contract
Backend-owned OpenAPI contract поступает во frontend как предоставленный
актуальный snapshot; generated `schema.d.ts` производен от него. Тип
request/response бери из generated `paths`, например:
```ts
type User = paths["/api/v1/auth/me"]["get"]["responses"][200]["content"]["application/json"];
```
Domain/UI type допустим, когда он отличается по смыслу, а не копирует transport DTO.
Generated TS вручную не исправляется. Не добавляй параллельный fetch/axios
client, runtime schema, hash/checksum registry или дополнительный drift checker
без отдельной текущей потребности; existing OpenAPI flow уже владеет генерацией. Frontend-команды не читают соседний
backend checkout и не проверяют backend runtime OpenAPI; изменение wire
shape/status/route сохраняет
публичный контракт предоставленного snapshot.
Одна UI-модель не требует менять API. `openapi:generate` работает offline от snapshot;
`openapi:check` запускает native `--check` того же CLI и падает при устаревших
generated types, не меняя tracked files.
## Auth и invalidation
Bearer header принадлежит shared authenticated fetch; query/mutation не задают
Authorization. Auth endpoints исключены из Bearer/retry middleware. Refresh
координируется single-flight; после `401` с `UNAUTHORIZED` возможен один retry.
Не добавляй собственный refresh/retry loop в caller.
Cookie-writing requests упорядочены только в текущей вкладке. Cross-tab auth
synchronization не требуется; допустим reload/re-login. Web Locks и отдельные
token stores не добавляются.
Protected queries используют existing session-aware boundary; logout очищает
session-scoped cache. Access token хранится только в памяти, refresh передаётся
httpOnly cookie с `credentials: include`. Не сохраняй tokens в localStorage,
URL, логах или fixtures. Logout очищает локальную session/cache и при ошибке
сети; до окончания auth bootstrap показывай neutral state. Перед auth change
прочитай shared client, session owner и их tests.
Успешная connection mutation коммитит подтверждённый status и сбрасывает financial
GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница
от обычного фонового refetch. Даже повторное сохранение того же token начинает
новую generation; поздний ответ прежней generation не попадает в новый кэш.
Отмена/reset принадлежат TanStack Query и generated queryFn с AbortSignal,
без HTTP request registry, URL-wide latest-read counters и access epochs.
Успешный HTTP с `data: null` остаётся query success; readiness выбирается по
data/metadata, без подмены query status и отдельного transport state.
Status синхронизации содержит только состояние работы. Resources читаются
через `sync=observe` во время работы и после завершения, без hash comparison.
Согласованность income entries/groups обеспечивает backend; frontend не
дублирует её runtime validator.
Mutations используют существующий generated client и query-key owner;
после успеха обновляй/invalidируй только связанные данные. Optimistic update
нужен конкретному UX и обязан иметь rollback/error behavior.
Default focus refetch выключен; freshness и retry задаются существующими
query options у entity/feature owner. После `staleTime` повторное открытие
экрана может запустить background refetch; focus сам по себе его не запускает.
При обычном refetch сохраняй Client Cached Snapshot, при session/connection
смене не показывай данные прежнего владельца. UI presentation — в UI convention.
## Errors
Извлекай code через existing `apiErrorCode` из shared/api. Backend Problem
Details code задаёт machine behavior; user-facing текст принадлежит i18n.
Network/unknown error получает безопасное локальное сообщение и retry,
не raw exception text. Input/domain failure сохраняет ввод и показывает
ошибку у ответственного form/section owner; не маскируй её успешным результатом.
Если одна группа API codes требует одинакового recovery в нескольких views,
сведи классификацию у ближайшего общего owner. Не размножай magic comparisons
и используй `ApiErrorCode` для известных значений: generated `code` остаётся `string`.
Неизвестный новый code должен сохранять fallback. Рефакторинг существующих
разных recovery paths требует behavior proof, а не общей обработки всех ошибок.
## Примеры и проверка
[usePortfolio](../../src/entities/portfolio/api/usePortfolio.ts) — entity query;
[shared client](../../src/shared/api/client.ts) — transport/auth boundary.
Изменяя header/retry/invalidation, проверяй наблюдаемое поведение через MSW и
[Testing](testing.md); не обращайся к реальному T-Bank.
`npm run openapi:check`, `npm run typecheck` и targeted tests проверяют разные
части решения. Для поведения API-клиента используй сценарии MSW на публичной
HTTP-границе; реализация сервера не является входом frontend-проверок.