El proyecto por dentro¶
Qué es de quién¶
Lo importante de un proyecto de tai-api es saber qué ficheros son tuyos —se crean una vez y
generate no los toca— y cuáles son del generador —se reescriben en cada generate, y
editarlos es perder el cambio—.
api/ ← raíz del proyecto
├── .taiapiproject configuración del proyecto tuyo (y de los comandos)
├── pyproject.toml dependencias del servicio tuyo; tai-api gestiona sus paquetes
├── dockerfile, entrypoint.sh la imagen tuyo (se crea una vez)
├── rbac/
│ ├── main.py los permisos del realm TUYO
│ └── tables/main/__init__.py constantes de permisos generado
└── api/ ← el paquete Python
├── config.py VERSION, ROOT_PATH, ALLOWED_ORIGINS TUYO
├── resources/ respuestas, excepciones, handlers tuyo (se copia una vez)
├── extensions/__init__.py setup(app): lo propio del arranque TUYO (lo creas tú)
├── __main__.py, __dev__.py los puntos de entrada generados
├── app/ create_app(), salud, documentación generado
├── database/<schema>/ el cliente de tai-sql generado
├── diagrams/ los diagramas ER generado
└── routers/
├── __init__.py main_router generado
├── database/<schema>/<tabla>/ los endpoints generado
├── auth/ login, dependencias, /rbac generado
└── custom/ tus endpoints TUYO
Todo fichero generado abre con una marca:
- Un fichero con la marca se reescribe siempre. Los endpoints,
app/yrouters/auth/. - Un fichero sin la marca no se toca. Si editas
__main__.py,__dev__.pyorouters/__init__.pyquitando la marca,generateavisa y lo deja como está, pero deja de actualizarlo. Lo tuyo va enextensions/yrouters/custom/. - Una carpeta que no reconoce, no la borra. Un router propio dentro de
routers/database/<schema>/sobrevive agenerate, pero no se monta:generateavisa de que lo muevas arouters/custom/. - Lo que desaparece del schema, se retira. Una tabla eliminada se lleva su carpeta de endpoints; un schema eliminado del proyecto de tai-sql, sus routers y su cliente.
La relación con tai-sql¶
tai-api no guarda la ruta del proyecto de tai-sql: lo busca cada vez desde el directorio actual,
hacia arriba por los padres y después hacia abajo por las subcarpetas. Y busca su propio
.taiapiproject hacia arriba y en las subcarpetas inmediatas. Por eso lo cómodo es ejecutar
desde el directorio que contiene a los dos:
Desde dentro de api/, tai-sql no encuentra un proyecto hermano. generate, auth, rbac (en
modo Database) y set-mcp lo necesitan; init, install y dev, no.
Si la API queda más abajo —en un proyecto de tai-chat está en chatbot/api/—, desde la raíz no
se encuentra. Se indica con --project-dir, que no cambia de directorio, así que tai-sql sigue
viendo database/:
tai-api --project-dir chatbot/api generate
TAI_API_PROJECT_DIR=chatbot/api tai-api rbac push # lo mismo, con la variable
Las dependencias¶
Hay dos pyproject.toml, igual que en tai-sql:
- El de tai-api (lo que instalas con
pip install 'tai-api[...]') es la herramienta. - El del proyecto es lo que necesita la API en producción. Lo escribe
init, ygenerate,authyset-mcpañaden o quitan solo los paquetes que gestiona tai-api —tai-keycloak, fastapi-mcp…— cuando cambia la configuración. Lo que añadas tú no se toca.
resources/ no se regenera¶
tai-api init copia resources/ —el envoltorio APIResponse, las excepciones y sus handlers,
el montaje de MCP— una vez. Es la única parte que tai-api entrega y abandona: una versión nueva
no la actualiza, y por eso ningún endpoint generado depende de algo que no estuviera ya.
Lo que sí hace generate es avisar cuando el resources/ del proyecto es de una revisión
anterior (RESOURCES_REVISION en su __init__.py; sin la marca, la 1), con lo que ha cambiado
desde entonces y la ruta de la copia nueva dentro del paquete. Ponerla al día es copiar los ficheros
—o lo que interese de ellos, si los editaste— y subir la marca.
| Revisión | Qué trae |
|---|---|
| 2 (tai-api 0.5.0) | Las rutas que no existen (404) y los métodos no admitidos (405) salen con el formato de siempre; las restricciones de la base de datos se clasifican por su SQLSTATE —un duplicado de MySQL o un NOT NULL ya no son un 500—; un t.abort() de un trigger de tai-sql es un 422 BUSINESS_RULE_VIOLATION; lo que llega en el cuerpo de una petición inválida no se repite en la respuesta ni en el log |