0007. Frontend React: stack y conexión con el backend¶
Estado: Propuesto Fecha: 2026-08-28 Autor: Duval Alcivar
Contexto¶
El backend es un conjunto de microservicios FastAPI con contratos OpenAPI
(ADR 0003) y autenticación JWT emitida por svc-identidad (ADR 0006). El
frontend (ERP y CRM) necesita un stack único y reglas de conexión con la
API: cómo lleva el token, cómo valida permisos en pantalla y cómo genera los
tipos de las respuestas sin duplicarlos a mano.
Decisión¶
Stack¶
| Área | Herramienta |
|---|---|
| Framework | React 18 |
| Bundler / dev server | Vite |
| Lenguaje | TypeScript en modo estricto (strict: true) |
| UI | MUI, con tema corporativo centralizado |
| Estado de servidor | TanStack Query (fetch, caché, invalidación) |
| Estado de interfaz | Zustand (filtros, sidebar, preferencias UI) |
| Formularios | react-hook-form + zod (validación declarativa y tipada) |
| HTTP | Axios centralizado, con interceptores para token y 401/403 |
| Tipos de API | Generados automáticamente desde el OpenAPI del backend |
| Testing | Vitest + React Testing Library (unidad/componentes) + Playwright (E2E) |
| Calidad | ESLint + Prettier + pre-commit (Husky + lint-staged) |
Cómo se conecta con el backend (auth + API)¶
sequenceDiagram
participant B as Navegador (app React)
participant I as svc-identidad
participant A as API (cualquier servicio)
B->>I: login (usuario + clave + empresa elegida)
I-->>B: access + refresh (empresa_id va DENTRO del token, no en la petición)
B->>A: GET /datos — Axios inyecta Authorization: Bearer (interceptor)
A-->>B: 200 respuestas / 401 / 403
B->>B: guard de ruta valida rol (espejo del backend, ADR 0006)
B->>B: 401 → refresh automático (o logout si el refresh expiró)
Reglas de conexión:
- El token nunca lo manda el código de negocio: el interceptor de Axios lo inyecta solo. Nunca en query, cuerpo o logs (ADR 0006).
- La empresa se elige al loguear desde el selector de empresas del
usuario;
empresa_idviaja dentro del token que emite identidad. El frontend nunca mandaempresa_idcomo parámetro libre (ADR 0002). - Tipos de API generados desde el OpenAPI (ADR 0003) en
src/types/; si un tipo no sale de ahí, se rechaza el PR. - Guards de ruta por rol, espejo de los permisos del backend: la UI oculta lo que no corresponde, pero el permiso real lo valida la API (defensa en dos capas).
Estructura de carpetas (por app)¶
app/
└── src/
├── app/ # router, guards, layout (sidebar, header)
├── core/ # axios, auth, tema MUI, utilidades
├── features/ # un módulo por dominio (ej. inventario, casos)
│ └── inventario/
│ ├── api/ # hooks de TanStack Query
│ ├── components/ # componentes de UI del módulo
│ ├── forms/ # react-hook-form + zod
│ └── store/ # Zustand (estado de UI del módulo)
└── types/ # tipos generados desde OpenAPI
Buenas prácticas (obligatorias en el PR)¶
strict: true— errores de tipo se detectan al compilar, no en producción.- TanStack Query para todo estado de servidor; nada de
useEffect+fetchad-hoc para datos. - react-hook-form + zod para formularios grandes: validación declarativa y tipada.
- Code-splitting por módulo/ruta: carga inicial liviana.
- Cobertura ≥75% en hooks y lógica de negocio (Vitest + RTL), con E2E de Playwright para los flujos críticos: login, crear/aprobar un caso, generar pedido.
- ESLint + Prettier vía Husky/lint-staged antes de commit.
Alternativas descartadas¶
- Next.js (SSR/SSG) — descartado: la app es una SPA que consume una API ya separada en servicios; Vite basta y hay menos superficie de despliegue.
- Redux Toolkit — descartado para estado de interfaz: más boilerplate que Zustand para lo que se maneja aquí (filtros, sidebar, preferencias).
- fetch nativo — descartado: los interceptores de Axios centralizan token, 401/403 y reintentos en un solo lugar.
- Tipos de API escritos a mano — descartado: se desincronizan del contrato; se generan del OpenAPI (ADR 0003).
Consecuencias¶
- Un solo lugar se encarga de auth y errores (core/axios); los módulos solo usan hooks de TanStack Query.
- El permiso se valida en dos capas (guard de UI + API), así la UI puede mentir pero la API no.
- Queda para el ADR de identidad (0006 + siguiente) el flujo exacto de refresh en el navegador y el manejo de la sesión en pestañas/recargas.