FastAPI yendo Full Stack
La historia de cómo Vue vio la serpiente y se enamoró.
FastAPI es una de esas piezas de software que me genera sospechas porque la historia del backend en Python es demasiado agradable. Los type hints en handlers estilo Flask/Express se convierten en parseo y validación. Async con auto reloads funciona sin invocar un manual arcano de configuración de framework. Súmale sus esquemas de OpenAPI y los auto reloads, y puedes pasar de git init a una API respetable antes de que la cafetera haya terminado su secuencia de arranque.
Entonces necesitas un frontend.
Históricamente, aquí es donde la autopista iluminada con neones terminaba abruptamente en un muro de hormigón.
FastAPI es muy bueno con las APIs, pero realmente no puede servir tu frontend en la raíz del sitio, a menos que quisieras que todas tus peticiones se enrutaran a StaticFiles. Y el soporte para SPA es inaudito. Podías servir archivos, renderizar plantillas o acoplar la maquinaria que quisieras, pero la última milla entre un frontend moderno de JavaScript y la aplicación de Python quedaba en gran parte como ejercicio para el lector.
Y ese lector era cada vez más yo.
Mientras tanto, en tierras del frontend
Yo uso Vue para este trabajo. Vue vive aproximadamente en la misma arena que React y Svelte: interfaces basadas en componentes, estado reactivo, enrutado del lado del cliente y la maquinaria moderna habitual para convertir una pila de archivos fuente en algo que un navegador pueda ejecutar.
El paquete fastapi-vue puede usarse también con otros, así que describiré brevemente a los contendientes.
React
React es el gorila de 800 libras. Tiene un ecosistema colosal, una respuesta para todo, y normalmente tres respuestas adicionales discutiendo con la primera.
Su mayor ventaja práctica es simplemente la gravedad: muchos desarrolladores lo conocen, muchas librerías apuntan a él, y mucho código de ejemplo comienza con npm install react. Es enormemente más popular que Vue, lo que hace que la integración relativamente escasa entre React y FastAPI sea bastante reveladora por sí misma.
Incluso la solución full-stack oficial de FastAPI — publicada por primera vez hace apenas unas semanas — es fundamentalmente un repositorio de plantilla: un stack de React preseleccionado desde el que partes, en lugar de una herramienta capaz de entrar en una aplicación Python existente y conectarle un frontend.
Svelte
Svelte adopta un enfoque más centrado en el compilador. En lugar de enviar tanta maquinaria del framework al navegador, transforma los componentes en tiempo de compilación. El resultado puede ejecutarse sin Node en producción.
Requiere un poco de trucos para lograr una reactividad correcta, exigiendo el uso de $ en su lenguaje personalizado para indicar qué necesita actualizarse. Por otro lado, este lenguaje personalizado permite escribir código más corto que el de sus competidores.
Vue
Vue se sitúa en un lugar que me gusta bastante: suficiente estructura para construir aplicaciones sustanciales, pero sin hacer que el HTML desaparezca bajo varias capas geológicas de JavaScript. La reactividad con su store Pinia simplemente funciona, y no estorba. Y cuando necesitas ese último ápice absoluto de rendimiento, se integra bien con estructuras no reactivas. Sin envoltorios complicados ni useState y demás boilerplate.
Y funciona rápido, lo cual no es el caso de la mayoría de las aplicaciones React.
Su herramienta oficial create-vue también me da algo particularmente útil aquí: no tengo que inventar mi idea de cómo debería verse un proyecto Vue. Puede preguntar interactivamente si la aplicación debería usar TypeScript, Vue Router, Pinia, Vitest, Playwright/Cypress, ESLint, Prettier y el resto del equipamiento habitual.
Eso se vuelve importante más adelante.
¿Cómo desplegamos realmente esta cosa?
Una vez que tengo FastAPI por un lado y Vue, React o Svelte por el otro, hay varias formas fundamentalmente distintas de poner la criatura resultante en un servidor.
Opción 1: rendirse ante Node
La respuesta obvia del mundo JavaScript es poner también un servidor JavaScript en producción.
Eso tiene mucho sentido si realmente quiero un backend en Node. Hay una ventaja genuina en tener un solo lenguaje y herramientas estrechamente relacionadas en ambos lados del cable. Si mi equipo quiere TypeScript en todas partes, esa es una arquitectura perfectamente coherente.
Yo no.
Elegí FastAPI porque quiero escribir el backend en Python. Instalar otro runtime al lado solo para servir los assets del frontend es como contratar a un segundo chef para llevar los platos de la cocina al comedor.
También está el pequeño asunto del universo de dependencias de JavaScript. Es demasiado pesado, mantiene poca compatibilidad y a menudo está roto. Aunque nvm puede instalar fácilmente la versión correcta de Node, aun así ocupa una enorme cantidad de espacio que los servidores alojados a menudo no tienen.
Node es útil en mi máquina de desarrollo. Vite es excelente. create-vue es excelente. El ecosistema de build hace su trabajo extremadamente bien.
Eso no significa que quiera nada de ello en la máquina de producción.
Opción 2: renderizar todo en el servidor
En el extremo opuesto puedo prescindir por completo del SPA y generar el HTML en Python.
Esto merece más respeto del que a veces recibe. No toda aplicación necesita un runtime de aplicación del lado del cliente, un router y varios megabytes de civilización de soporte.
Herramientas como htmx pueden llevar el HTML renderizado en el servidor sorprendentemente lejos al añadir peticiones HTTP, intercambios, transiciones, WebSockets y eventos enviados por el servidor directamente a través de atributos HTML. Combínalo con html5tagger para crear esos documentos y fragmentos de HTML en el servidor, y tendrás un sitio que funciona en todas partes y que puede ser leído por redes sociales, buscadores y agentes por igual.
Para sitios dominados por documentos, formularios e interacciones relativamente simples, me gusta esta arquitectura. El navegador pide algo, Python produce HTML, y nadie necesita un pipeline de build capaz de lanzar una sonda a Marte.
Vasanko.com está construido con este método, añadiendo Vue solo para la interfaz de administración, donde se requiere mayor interactividad. Este CMS funciona con una configuración FastAPI–Vue, pero renderiza todas las páginas en el servidor con Python.
Opción 3: ejecutar dos servidores
El compromiso habitual es, por tanto:
- FastAPI ejecuta la API.
- Vite u otro servidor de JavaScript ejecuta el frontend
- Un proxy inverso coloca ambos bajo un mismo hostname.
- Producción ahora tiene dos aplicaciones, dos pilas de dependencias, dos procesos y otro archivo de configuración manteniendo unido el matrimonio.
Esto funciona, pero no es bonito.
Al navegador no le importa si app.js fue emitido por Python, Node, Caddy, nginx o una tostadora suficientemente decidida. Una vez que el frontend ha sido compilado, el resultado es HTML, CSS, JavaScript, fuentes e imágenes estáticos.
Entonces, ¿por qué mantener la fábrica en funcionamiento después de que el producto ya ha salido de la línea de montaje?
Construye con JavaScript, ejecuta con Python
Eso se convirtió en la idea central detrás de fastapi-vue-setup.
Quiero las herramientas de JavaScript donde realmente son útiles:
create-vuepara crear el frontend- Vite para el servidor de desarrollo
- Recargas en caliente instantáneas ante los cambios
- La compilación normal de Vue en Node, Bun o Deno
Y luego quiero que desaparezca.
El artefacto de producción debería ser un paquete de Python que contenga un frontend ya compilado. Instalar ese paquete no debería requerir Node, npm, Vite, el código fuente de Vue, ni la anomalía gravitacional tradicionalmente almacenada bajo node_modules.
Así es exactamente como fastapi-vue-setup construye proyectos hoy: uv build ejecuta la compilación del frontend e incluye los assets resultantes dentro del paquete de Python. La configuración de Hatch generada trata frontend-build como un artefacto y se engancha a la compilación de la distribución fuente, a la vez que limita el contenido del paquete al paquete de Python en sí.
La distinción importa especialmente para un sdist. No quiero que una distribución fuente signifique “aquí hay algo de Python, aquí hay un árbol de desarrollo completo de Vue, ahora instala Node y reconstruye la aplicación real por tu cuenta”. El frontend ya está compilado antes de que la distribución salga de mi máquina.
El paquete contiene lo que necesita para ejecutarse, no todo lo que yo necesité para compilarlo.
Eso deja el despliegue con un aspecto maravillosamente aburrido:
uvx my-app
Sin ceremonia de instalación de Node en la máquina de destino. Sin npm install. Sin servidor de frontend. Sin árbol de dependencias de JavaScript despertando tres meses después y pidiendo que lo alimenten.
El paquete inicia FastAPI, y FastAPI sirve la aplicación.
El otro problema feo
Construir el frontend fue solo la mitad del trabajo.
StaticFiles no es exactamente un servidor de frontend
FastAPI expone StaticFiles de Starlette, y para los recursos estáticos habituales hace exactamente lo que promete:
app.mount("/static", StaticFiles(directory="static"))
El problema empieza cuando el frontend es el sitio en sí.
Una aplicación Vue normalmente quiere /. También puede querer /login, /settings, /dashboard/coffee-reactor/7, y cualquier otra ruta que pertenezca al router del lado del cliente.
Pero montar StaticFiles en / en la práctica le entrega a esa aplicación montada todo el espacio de URLs restante. El propio mantenedor de FastAPI explicó el problema hace años: móntalo en la raíz y la aplicación estática toma el control, por lo que las operaciones de ruta normales debajo de ella dejan de funcionar como se espera.
Y necesitamos:
- Build del frontend en
/* - FastAPI sigue gestionando
/api/.../openapi.json- cualquier otra ruta arbitraria
Este no es el mismo problema que servir /static/logo.svg.
FastAPI ha mejorado muy recientemente su propio soporte de frontend, y la documentación actual ahora apunta explícitamente a las aplicaciones frontend hacia app.frontend() en lugar de StaticFiles simple. Pero fastapi-vue es anterior a esa solución y también tiene un trabajo algo diferente: es la pequeña mitad runtime de este sistema completo de empaquetado.
Runtime fastapi-vue
El paquete complementario fastapi-vue proporciona un manejador Frontend personalizado en lugar de montar StaticFiles sobre la aplicación.
En modo SPA puede devolver index.html para rutas que pertenecen al router del lado del cliente, en lugar de requerir un archivo físico con ese nombre.
Note
Debido a cómo funciona el enrutamiento de FastAPI, los montajes de la app se tragan todo lo que esté bajo esa ruta. El resto de las rutas se prueban en orden y gana la primera. Por lo tanto, en modo SPA debemos colocar el catch-all en último lugar en el módulo de la app.
Con el modo SPA desactivado, solo se vincula a rutas de archivos reales, permitiendo que tus rutas posteriores sigan capturando lo que se escape.
También se encarga de los detalles menos glamurosos que no tengo ninguna gana de reimplementar cada martes:
ETagyLast-Modified- Caché inmutable para los assets compilados
- Caché en RAM con compresión zstd
- Fallback de enrutado SPA y
/favicon.icocuando se necesita - No servir accidentalmente una build obsoleta en modo dev
Esta es la dependencia de runtime para producción. Es diminuta y solo Python.
Toda la maquinaria para compilar y desarrollar la aplicación Vue permanece en el proyecto fuente.
Presentamos fastapi-vue-setup
Con esas piezas en su lugar, fastapi-vue-setup se encarga principalmente de eliminar el cableado repetitivo.
El comando básico es deliberadamente poco emocionante: apúntalo a la carpeta de tu aplicación, o a . si ya estás ahí.
uvx fastapi-vue-setup my-app
Need to change the port numbers your prod and vite and dev backend use by default? Run with --ports. Without that it retains the ports you previously had configured. And you can always --listen on your CLI or scripts/devserver.py to change that at runtime.
Pero hay dos casos bastante diferentes escondidos detrás de esto.
Y proporciona utilidades de soporte para el punto de entrada de la CLI que obtiene tu aplicación, que a su vez puede aceptar tus propias opciones de línea de comandos, algo que fastapi run no puede ofrecer.
Creando una nueva aplicación
Para un proyecto nuevo no quiero que fastapi-vue-setup imponga una plantilla congelada de Vue de mi propia creación. Quiero que cree un proyecto de Python configurado con el nombre que le di, y la configuración de Vue que yo elija:
- JavaScript o TypeScript
- Vue Router o sin router
- Pinia o no
- Opciones de testing, linting y formato
- Las demás opciones que admite el
create-vueactual
La herramienta construye entonces la integración de FastAPI alrededor de la aplicación que el desarrollador realmente eligió.
Esto importa porque una plantilla inevitablemente fosiliza las preferencias de alguien. Seis meses después, su idea de un modern full stack puede que ya pertenezca a una vitrina de museo, y tú te quedas atascado con lo que te tocó.
Prefiero poder elegir.
¿Ya tienes una aplicación?
Aquí puede que ya tenga un proyecto real con código que preferiría no vaporizar. Puede crearse con el mismo script o existir por separado, y el script lo parcheará donde sea necesario.
El script de setup detecta el proyecto Python y el módulo de backend, detecta o crea el frontend Vue, y parchea las piezas que puede integrar de forma segura con cambios mínimos.
Note
Mueve primero tu Vue existente completamente bajo frontend/. Colocamos Vue ahí para evitar contaminar la raíz con cosas de Node. Colócalo ahí para que el script parchee la app existente en lugar de crear una nueva.
¿Necesitas actualizar a la última versión? Simplemente ejecuta fastapi-vue-setup de nuevo y parcheará las nuevas funcionalidades que tenga.
Esa es una de las principales diferencias entre esto y un repositorio de plantilla.
Una plantilla dice:
Haz fork a este repositorio para empezar tu proyectoYo necesitaba una herramienta que también pueda decir:
Bien, ya llevas 30.000 líneas. Muéstrame dónde está el paciente.
Tu aplicación está lista para ejecutarse
uv run scripts/devserver.py
vite.config.js
Configure which paths are proxied to the backend. By default this just /api. This configuration only affects the dev setup, in production everything lands on FastAPI.
JS_RUNTIME
Environment with value bun/deno/node or path to one of them (otherwise we find one).
Eso inicia el servidor de desarrollo de Vue/Vite y FastAPI con soporte de recarga. La integración de Vite generada actúa como proxy de las peticiones al backend hacia el servidor de desarrollo de Python. Tu navegador se conecta a Vite.
Así que durante el desarrollo sigo teniendo las cosas que realmente me gustan del ecosistema de JavaScript.
Cuando el desarrollo esté terminado:
uv build # Installs and (re)builds everything
my-app # CLI entry point provided (in .venv)
Vue está compilado, el resultado se integra en la distribución de Python, y la maquinaria de build ha completado su misión. Puedes ejecutar uv publish en tu paquete si así lo deseas, o simplemente copiar a producción desde dist/, luego instalar y ejecutar con:
uv tool install my-app-0.1.0.tar.gz
my-app
Lo único que necesité instalar previamente fue UV y, en el sistema de desarrollo, Node en sí. No tuve que tocar npm en absoluto y ahora puedo instalarlo o ejecutarlo en otro lugar sin llevarme el taller de JavaScript de paseo.
Una única aplicación, por fin
Lo que quería en última instancia no era nada particularmente exótico.
- Quería escribir el backend en Python.
- Quería escribir el frontend en Vue.
- Quería usar Vite durante el desarrollo.
Y quería configurar y desplegar una sola cosa rápidamente.
No una aplicación Python más una aplicación JavaScript. No dos contenedores unidos por nginx y desconfianza mutua.
Un paquete Python de <1MB que Simplemente Funciona™.
Contiene el frontend. FastAPI lo sirve desde la raíz del sitio sin tragarse el resto de la aplicación. El routing de Vue funciona cuando quiero una SPA, y el routing simple por archivos permite que mi backend se quede con los catch-alls para sí mismo, por ejemplo, manejando cualquier URL bonita con contenido renderizado del lado del servidor como en este sitio. Ese, a su vez, puede usar Vue en los lugares donde lo necesite, dos mundos en perfecta armonía.
La toolchain de JavaScript hace lo que una toolchain debe hacer:
compila el software y luego se quita del camino.
A continuación, tu stack completo necesita una base de datos y Kanta te cubre. Con esta cadena de herramientas tendrás tu aplicación funcionando en un abrir y cerrar de ojos.