Saltar a contenido

El cliente TypeScript

tai-api generate escribe, dentro de tu frontend, todo lo que hace falta para hablar con la API: los tipos de cada tabla, una función por endpoint, la sesión ya resuelta, los permisos, las consultas de TanStack Query, los esquemas de los formularios y el cuadro de luces de las pantallas —qué pantallas hay, cuáles están activas y qué permiso pide cada una—.

Si cambia el schema o el RBAC, tai-api generate cambia la API y el cliente en la misma ejecución, y el compilador de TypeScript te señala en el frontend lo que se rompió.

La regla de oro

El cliente no se edita: se regenera. Lo tuyo —tus pantallas, tu declaración de rutas, tus componentes— vive fuera de su carpeta y lo importa.

Un núcleo y un adaptador por framework

El cliente está hecho en dos capas, para que sirva igual en React, en Svelte o en cualquier otro framework de TypeScript:

  • El núcleo, que no depende de nada: las llamadas, la sesión, los permisos y la lógica del cuadro de luces (qué hacer en cada URL, el menú, las migas). Es el mismo para todos.
  • Un adaptador por framework, que solo pone el núcleo en su idioma: en React, un provider, unos hooks y las rutas de react-router; en Svelte, el contexto y unos stores. Los dos adaptadores son gemelos: las mismas piezas, con los mismos nombres donde se puede, y deciden lo mismo con la misma declaración.

Para otro framework —Vue, Solid, Angular…— no hace falta esperar a que exista su adaptador: el núcleo es lo que usan los otros dos, y uno propio son unas pocas líneas. Ver Otro framework.

El protocolo, paso a paso

Un frontend con el cliente se monta siempre igual, sea cual sea el framework. Cada página de esta sección es un paso, y en cada una el código está en pestañas: React, Svelte y, donde hace falta, Otro framework. Elige la tuya en cualquier página y se queda elegida en todas.

Paso Qué se hace Dónde
1 Activar el cliente en la API y generarlo abajo
2 Una instancia del cliente y la sesión en la raíz de la aplicación; la página de login Montar la aplicación
3 Declarar las pantallas: ruta, nombre, icono, activa o en construcción y permiso Las pantallas
4 Protegerlas, y sacar de la misma declaración el menú y las migas Las pantallas
5 La sesión y los permisos dentro de cada pantalla La sesión y los permisos
5b Si la aplicación administra usuarios: api.rbac(realm) La gestión de usuarios
6 Pedir datos a la API, con sus tipos Llamadas y tipos
7 La caché de las consultas TanStack Query
8 Los formularios Formularios
9 Servirlo junto a la API Despliegue

Si tu frontend ya tiene la autenticación escrita a mano, Migrar dice qué se borra y qué se queda; si ya usa el cliente de tai-api 0.6, qué cambia (Desde el cliente 0.6).

Activarlo

En el .taiapiproject de la API, el bloque frontend:

{
  "frontend": {
    "output_dir": "../app/frontend/src/lib/api",
    "base_path": "/api",
    "tanstack": true,
    "zod": true,
    "svelte": true
  }
}
Clave Qué es Por defecto
output_dir Dónde se escribe, relativo al .taiapiproject obligatorio
base_path De dónde cuelga la API vista desde el navegador /api
tanstack Las consultas y mutaciones de TanStack Query de cada tabla true
zod Los esquemas zod de alta y edición de cada tabla true
react El adaptador de React, con react-router false
svelte El adaptador de Svelte (Svelte 4 o 5, con SvelteKit o sin él) false

Se pueden activar los dos adaptadores, o ninguno: el núcleo siempre se genera. Después, tai-api generate. Sin el bloque no se escribe nada fuera de la API.

Con autenticación, exige la sesión con cookie

El cliente guarda el access token en memoria y el refresh en una cookie que el JavaScript no puede leer. Por eso generate se niega si la API tiene autenticación y no declara "auth": { …, "session": { "transport": "cookie" } }. Ver La sesión con cookie.

Lo que escribe

src/lib/api/                 ← output_dir: es del generador, no lo toques
├── index.ts                 createTaiClient(), PERMISSIONS, hasPermission y los tipos
├── vite.ts                  taiApiProxy(): el proxy /api para Vite
├── core/                    el fetch, los errores y los tipos comunes
├── auth/
│   ├── session.ts           login, logout, refresco y la vista de la sesión (sessionView)
│   └── permissions.ts       PERMISSIONS, sacado de tus rbac/<realm>.py, y can / hasPermission
├── routes/                  el cuadro de luces sin framework: access, navigation, breadcrumbs
├── react/                   con "react": true — TaiProvider, useSession, renderRoutes, useNavigation…
├── svelte/                  con "svelte": true — setTaiClient, getSession, accessStore, navigationStore…
└── <schema>/                uno por schema: public/, …
    ├── models.ts            los tipos: LoteRead, LoteCreate, LoteUpdateValues, LoteFilters…
    ├── resources.ts         una función por endpoint
    ├── query.ts             opciones de TanStack Query
    └── zod.ts               esquemas de formulario

Cada fichero abre con una marca de «no editar». Un fichero sin ella no se reescribe (se avisa), y puedes tener tus propios ficheros en la carpeta: no se tocan.

Lo que necesita tu frontend

El núcleo no depende de nada, y compila con el tsconfig que crea Vite para un proyecto nuevo (react-ts y svelte-ts: erasableSyntaxOnly, noUnusedLocals, verbatimModuleSyntax…) y con lo más estricto que se le puede sumar (noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverride). Lo demás, solo si lo activas:

Para React Svelte
El adaptador react 19 y react-router-dom 7 svelte 4 o 5
"tanstack": true @tanstack/react-query 5 @tanstack/svelte-query
"zod": true zod 4 zod 4

tai-api generate nunca ejecuta npm, pero lee el package.json más cercano a output_dir: si el bloque frontend activa algo que el frontend no tiene —"svelte": true en un proyecto de React, "zod": true sin zod— falla antes de escribir nada y dice qué instalar o qué quitar, porque ese cliente no compilaría. Sin package.json, avisa y genera. "tanstack": true no se comprueba: query.ts no importa TanStack.

Las piezas, en cada framework

Para Núcleo (cualquier framework) React Svelte
Crear el cliente createTaiClient()
Ponerlo al alcance de la aplicación y recuperar la sesión api.session.restore() <TaiProvider client={api}> setTaiClient(api)
El cliente desde un componente useTai() getTai()
La sesión como estado sessionView(api.session) + api.session.subscribe useSession() getSession() → $session
Un permiso hasPermission(user, p) hasPermission(p), <Can permission> $session.hasPermission(p)
Declarar las pantallas defineRoutes defineRoutes (con element) defineRoutes
Qué hacer en una URL access(…) renderRoutes(routes, { layout, … }) accessStore(routes, page)
El menú navigation(…) useNavigation(routes) navigationStore(routes)
Las migas y el título breadcrumbs(…), currentRoute(…) useBreadcrumbs, useCurrentRoute breadcrumbsStore, currentRouteStore
Volver tras el login safeReturnPath(from) location.state.from ?from= + safeReturnPath