FastAPI geht Full Stack
Die Geschichte, wie Vue die Schlange sah und sich verliebte.
FastAPI ist eine dieser Softwarekomponenten, die mich misstrauisch machen, weil die Python-Backend-Story zu angenehm ist. Type Hints in Flask/Express-artigen Handlern werden zu Parsing und Validierung. Async mit Auto-Reloads funktioniert, ohne ein unheimliches Framework-Konfigurationshandbuch beschwören zu mĂŒssen. Mit den OpenAPI-Schemas und Auto-Reloads obendrauf kommt man von git init zu einer respektablen API, bevor die Kaffeemaschine ihre Boot-Sequenz abgeschlossen hat.
Dann brauchst du ein Frontend.
Bisher endete die neonbeleuchtete SchnellstraĂe an dieser Stelle abrupt an einer Betonwand.
FastAPI ist sehr gut fĂŒr APIs, aber es kann Ihr Frontend nicht wirklich an der Site-Root ausliefern, es sei denn, Sie wollten alle Ihre Requests an StaticFiles routen. Und SPA-Support ist unbekannt. Man konnte Dateien ausliefern, Templates rendern oder beliebige andere Mechanismen anhĂ€ngen, aber die letzte Meile zwischen einem modernen JavaScript-Frontend und der Python-Anwendung wurde weitgehend dem Leser als Ăbung ĂŒberlassen.
Und dieser Leser war zunehmend ich selbst.
WĂ€hrenddessen im Frontend-Land
FĂŒr diese Aufgabe verwende ich Vue. Vue spielt in etwa derselben Liga wie React und Svelte: komponentenbasierte Interfaces, reaktiver State, clientseitiges Routing und das ĂŒbliche moderne RĂŒstzeug, um aus einem Haufen Quelldateien etwas zu machen, das ein Browser ausfĂŒhren kann.
Das Paket fastapi-vue lÀsst sich jedoch auch mit anderen verwenden, daher beschreibe ich die Kandidaten kurz.
React
React ist der 800-Pfund-Gorilla. Es hat ein gigantisches Ăkosystem, eine Antwort auf alles â und meist drei weitere Antworten, die der ersten widersprechen.
Sein gröĂter praktischer Vorteil ist schlicht die Anziehungskraft: Viele Entwickler kennen es, viele Bibliotheken zielen darauf ab, und viel Beispielcode beginnt mit npm install react. Es ist enorm viel populĂ€rer als Vue, was die relativ dĂŒnne Integration zwischen React und FastAPI an sich schon recht bezeichnend macht.
Selbst die offizielle FastAPI-Full-Stack-Lösung â erst vor wenigen Wochen veröffentlicht â ist im Kern ein Template-Repository: ein vorausgewĂ€hlter React-Stack, von dem man ausgeht, statt eines Toolings, das in eine bestehende Python-Anwendung hineingehen und dort ein Frontend einbinden könnte.
Svelte
Svelte verfolgt einen stÀrker Compiler-lastigen Ansatz. Anstatt so viel Framework-Machinerie in den Browser auszuliefern, transformiert es Komponenten zur Build-Zeit. Das Ergebnis kann in der Produktion auch ohne Node laufen.
Es erfordert einiges an Trickserei fĂŒr korrekte ReaktivitĂ€t, denn in seiner eigenen Sprache muss $ angeben, was ein Update braucht. Auf der anderen Seite ermöglicht diese eigene Sprache kĂŒrzeren Code als die der Konkurrenz.
Vue
Vue sitzt an einer Stelle, die mir sehr gefÀllt: genug Struktur, um substanzielle Anwendungen zu bauen, aber ohne dass das HTML unter mehreren geologischen Schichten JavaScript verschwindet. Die ReaktivitÀt mit ihrem Pinia-Store funktioniert einfach und bleibt im Hintergrund. Und wenn man das absolut letzte QuÀntchen Performance braucht, lÀsst es sich gut mit nicht-reaktiven Strukturen kombinieren. Kein kompliziertes Wrapping oder useState und Àhnlicher Boilerplate nötig.
Und es lÀuft schnell, was bei den meisten React-Apps nicht der Fall ist.
Sein offizielles create-vue-Tool gibt mir hier auĂerdem etwas besonders NĂŒtzliches: Ich muss nicht meine eigene Vorstellung davon erfinden, wie ein Vue-Projekt aussehen sollte. Es kann interaktiv fragen, ob die Anwendung TypeScript, Vue Router, Pinia, Vitest, Playwright/Cypress, ESLint, Prettier und den Rest der ĂŒblichen Ausstattung verwenden soll.
Das wird spÀter wichtig.
Wie deployen wir das Ganze eigentlich?
Sobald ich FastAPI auf der einen Seite und Vue, React oder Svelte auf der anderen habe, gibt es ein paar grundlegend verschiedene Wege, wie ich die entstandene Kreatur auf einen Server bringen kann.
Option 1: Kapitulation vor Node
Die naheliegende Antwort aus der JavaScript-Welt ist, auch in der Produktion einen JavaScript-Server einzusetzen.
Das ergibt durchaus Sinn, wenn ich tatsĂ€chlich ein Node-Backend haben möchte. Es ist ein echter Vorteil, auf beiden Seiten der Leitung eine Sprache und eng verwandtes Tooling zu haben. Wenn mein Team ĂŒberall TypeScript einsetzen will, ist das eine völlig kohĂ€rente Architektur.
Ich nicht.
Ich habe FastAPI gewĂ€hlt, weil ich das Backend in Python schreiben möchte. Eine zusĂ€tzliche Runtime daneben zu installieren, nur um Frontend-Assets auszuliefern, fĂŒhlt sich an, als wĂŒrde man einen zweiten Koch einstellen, um Teller von der KĂŒche in den Speisesaal zu tragen.
Dann wĂ€re da noch die kleine Angelegenheit des JavaScript-Dependency-Universums. Es ist zu schwer, pflegt kaum KompatibilitĂ€t und ist oft kaputt. Zwar kann nvm problemlos die richtige Node-Version installieren, doch es beansprucht immer noch eine gewaltige Menge Speicherplatz, ĂŒber den gehostete Server oft nicht verfĂŒgen.
Node ist auf meinem Entwicklungsrechner nĂŒtzlich. Vite ist exzellent. create-vue ist exzellent. Das Build-Ăkosystem erledigt seine Arbeit auĂerordentlich gut.
Das heiĂt nicht, dass ich irgendetwas davon auf dem Produktionsserver haben möchte.
Option 2: alles auf dem Server rendern
Am anderen Extrem kann ich die SPA komplett weglassen und HTML in Python generieren.
Das verdient mehr Respekt, als ihm manchmal entgegengebracht wird. Nicht jede Anwendung braucht eine clientseitige Runtime, einen Router und mehrere Megabyte an unterstĂŒtzender Zivilisation.
Tools wie htmx können serverseitig gerendertes HTML ĂŒberraschend weit bringen, indem sie HTTP-Requests, Swaps, Transitionen, WebSockets und Server-Sent Events direkt ĂŒber HTML-Attribute hinzufĂŒgen. Kombiniere das mit html5tagger, um diese Dokumente und HTML-Fragmente auf dem Server zu erzeugen, und du erhĂ€ltst eine Website, die ĂŒberall lĂ€uft und die gleichermaĂen von Social Media, Suchmaschinen und Agenten gelesen werden kann.
FĂŒr Seiten, die von Dokumenten, Formularen und relativ einfachen Interaktionen dominiert werden, mag ich diese Architektur. Der Browser fragt etwas an, Python erzeugt HTML, und niemand braucht eine Build-Pipeline, mit der man eine Mars-Sonde starten könnte.
Vasanko.com basiert auf dieser Methode und ergĂ€nzt Vue nur fĂŒr das Admin-Interface, wo stĂ€rkere InteraktivitĂ€t gefragt ist. Dieses CMS lĂ€uft auf einem FastAPIâVue-Setup, rendert aber alle Seiten serverseitig in Python.
Option 3: zwei Server betreiben
Der ĂŒbliche Kompromiss ist daher:
- FastAPI betreibt die API.
- Vite oder ein anderer JavaScript-Server betreibt das Frontend
- Ein Reverse Proxy fasst beides unter einem Hostnamen zusammen.
- Die Produktion hat nun zwei Anwendungen, zwei Dependency-Stacks, zwei Prozesse und eine weitere Konfigurationsdatei, die die Ehe zusammenhÀlt.
Das funktioniert, ist aber nicht schön.
Dem Browser ist es egal, ob app.js von Python, Node, Caddy, nginx oder einem ausreichend entschlossenen Toaster ausgeliefert wurde. Sobald das Frontend gebaut ist, ist das Ergebnis statisches HTML, CSS, JavaScript, Fonts und Bilder.
Warum also die Fabrik weiterlaufen lassen, wenn das Produkt das FlieĂband lĂ€ngst verlassen hat?
Bauen mit JavaScript, ausfĂŒhren mit Python
Das wurde zur zentralen Idee hinter fastapi-vue-setup.
Ich möchte JavaScript-Tooling dort, wo es tatsĂ€chlich nĂŒtzlich ist:
create-vuezum Erstellen des Frontends- Vite fĂŒr den Dev-Server
- Sofortige Hot-Reloads bei Ănderungen
- Der normale Vue-Build auf Node, Bun oder Deno
Und dann will ich es los sein.
Das Produktionsartefakt sollte ein Python-Paket sein, das ein bereits gebautes Frontend enthĂ€lt. Die Installation dieses Pakets sollte weder Node, npm, Vite, noch Vue-Quellcode erfordern â und auch nicht die gravitative Anomalie, die traditionell unter node_modules abgelegt ist.
Genau so baut fastapi-vue-setup Projekte heute: uv build fĂŒhrt den Frontend-Build aus und bindet die resultierenden Assets in das Python-Paket ein. Die generierte Hatch-Konfiguration behandelt frontend-build als Artefakt und hakt sich in den Source-Distribution-Build ein, wĂ€hrend der Paketinhalt auf das Python-Paket selbst beschrĂ€nkt bleibt.
Die Unterscheidung ist besonders bei einer sdist wichtig. Ich möchte nicht, dass eine Source-Distribution bedeutet: âHier ist etwas Python, hier ist ein kompletter Vue-Entwicklungsbaum, jetzt installiere bitte Node und rekonstruiere die eigentliche Anwendung selbst." Das Frontend ist bereits kompiliert, bevor die Distribution meinen Rechner verlĂ€sst.
Das Paket enthĂ€lt, was es zum AusfĂŒhren braucht, nicht alles, was ich zufĂ€llig zum Bauen brauchte.
Damit sieht das Deployment wunderbar langweilig aus:
uvx my-app
Keine Node-Installationszeremonie auf der Zielmaschine. Kein npm install. Kein Frontend-Server. Kein JavaScript-AbhĂ€ngigkeitsbaum, der drei Monate spĂ€ter aufwacht und gefĂŒttert werden will.
Das Paket startet FastAPI, und FastAPI stellt die Anwendung bereit.
Das andere hÀssliche Problem
Das Frontend zu bauen war nur die halbe Miete.
StaticFiles ist nicht ganz ein Frontend-Server
FastAPI stellt Starlettes StaticFiles bereit, und fĂŒr gewöhnliche statische Assets macht es genau das, was der Name verspricht:
app.mount("/static", StaticFiles(directory="static"))
Die Probleme beginnen, wenn das Frontend die Website selbst ist.
Eine Vue-Anwendung will normalerweise /. Sie will möglicherweise auch /login, /settings, /dashboard/coffee-reactor/7 und alles andere, was dem clientseitigen Router gehört.
Aber das Mounten von StaticFiles auf / ĂŒbergibt dieser gemounteten Anwendung effektiv den gesamten verbleibenden URL-Raum. FastAPIs eigener Maintainer hat das Problem vor Jahren erklĂ€rt: Mountet man sie am Root, ĂŒbernimmt die statische Anwendung die Kontrolle, sodass die normalen Path-Operationen darunter nicht mehr wie erwartet funktionieren.
Und wir brauchen:
- Frontend-Build unter
/* - FastAPI verarbeitet weiterhin
/api/.../openapi.json- beliebige andere Routen
Das ist nicht dasselbe Problem wie das Ausliefern von /static/logo.svg.
FastAPI hat erst vor Kurzem seinen eigenen Frontend-Support verbessert, und die aktuelle Dokumentation verweist Frontend-Anwendungen nun explizit auf app.frontend() statt auf das einfache StaticFiles. Aber fastapi-vue ist Ă€lter als diese Lösung und hat auĂerdem eine etwas andere Aufgabe: Es ist die kleine Runtime-HĂ€lfte dieses kompletten Packaging-Systems.
Runtime fastapi-vue
Das Begleitpaket fastapi-vue stellt einen eigenen Frontend-Handler bereit, anstatt StaticFiles ĂŒber die Anwendung zu mounten.
Im SPA-Modus kann es index.html fĂŒr Pfade zurĂŒckgeben, die zum clientseitigen Router gehören, anstatt eine physische Datei mit diesem Namen zu verlangen.
Note
Aufgrund der Funktionsweise des FastAPI-Routings verschlingen App-Mounts alles unterhalb ihres Pfads. Die ĂŒbrigen Routen werden der Reihe nach geprĂŒft, und die erste passende gewinnt. Daher mĂŒssen wir im SPA-Modus die Catch-all-Route an letzter Stelle im App-Modul platzieren.
Mit deaktiviertem SPA-Modus bindet sie sich nur an Pfade tatsÀchlich vorhandener Dateien, sodass nachfolgende Routen weiterhin alles abfangen können, was durchfÀllt.
Es kĂŒmmert sich auĂerdem um die weniger glamourösen Details, die ich nicht besonders Lust habe, jeden Dienstag neu zu implementieren:
ETagundLast-Modified- Immutable Caching fĂŒr gebaute Assets
- RAM-Caching mit zstd-Kompression
- SPA-Routing-Fallback und
/favicon.icobei Bedarf - Kein versehentliches Ausliefern veralteter Builds im Devmode
Das ist die Production-Runtime-AbhÀngigkeit. Sie ist winzig und rein in Python geschrieben.
Die gesamte Maschinerie zum Bauen und Entwickeln der Vue-Anwendung bleibt im Quellprojekt.
Auftritt: fastapi-vue-setup
Wenn diese Voraussetzungen erfĂŒllt sind, besteht die Hauptaufgabe von fastapi-vue-setup darin, die repetitive Verdrahtung zu beseitigen.
Der Basisbefehl ist bewusst unspektakulÀr: Du gibst ihm den Ordner deiner App an, oder ., wenn du bereits dort bist.
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.
Aber dahinter verbergen sich zwei recht unterschiedliche FĂ€lle.
Und es bietet Hilfsprogramme fĂŒr den CLI-Einstiegspunkt, den Ihre App erhĂ€lt, der dann auch eigene Kommandozeilenoptionen entgegennehmen kann â etwas, das fastapi run nicht bieten kann.
Eine neue Anwendung erstellen
FĂŒr ein neues Projekt möchte ich nicht, dass fastapi-vue-setup mir eine starre Vue-Vorlage aus meiner eigenen Feder aufzwingt. Ich möchte, dass es ein Python-Projekt erstellt, das mit dem von mir vergebenen Namen und dem von mir gewĂ€hlten Vue-Setup konfiguriert ist:
- JavaScript oder TypeScript
- Vue Router oder kein Router
- Pinia oder nicht
- Auswahl fĂŒr Testing, Linting und Formatierung
- Die weiteren Optionen, die das aktuelle
create-vueunterstĂŒtzt
Das Tool baut dann die FastAPI-Integration um die Anwendung herum, die der Entwickler tatsÀchlich gewÀhlt hat.
Das ist wichtig, weil ein Template zwangslÀufig die Vorlieben von irgendjemandem konserviert. Sechs Monate spÀter gehört dessen Vorstellung eines modernen Full-Stacks vielleicht schon in eine Museums-Vitrine, und du sitzt auf dem fest, was du bekommen hast.
Ich hÀtte lieber die Wahl.
Du hast schon eine App?
Hier habe ich möglicherweise bereits ein echtes Projekt mit Code, den ich nicht einfach vernichten möchte. Es kann mit demselben Skript erstellt worden sein oder eigenstÀndig existieren, und das Skript patcht es dort, wo es nötig ist.
Das Setup-Skript erkennt das Python-Projekt und das Backend-Modul, erkennt oder erstellt das Vue-Frontend und patcht die Teile, die es mit minimalen Ănderungen sicher integrieren kann.
Note
Verschieben Sie Ihr bestehendes Vue zuerst vollstÀndig unter frontend/. Wir platzieren Vue dort, um das Root-Verzeichnis nicht mit Node-Dingen zu verunreinigen. Platzieren Sie es dort, damit das Skript die bestehende App patcht, statt eine neue zu erstellen.
MĂŒssen Sie auf die neueste Version upgraden? FĂŒhren Sie einfach fastapi-vue-setup erneut aus, und es patcht die neu hinzugekommenen Features hinein.
Das ist einer der wesentlichen Unterschiede zwischen diesem und einem Template-Repository.
Ein Template sagt:
Forke dieses Repository, um dein Projekt zu startenIch brauchte ein Tooling, das auch sagen kann:
Schon gut, du bist bereits 30.000 Zeilen weit. Zeig mir, wo der Patient ist.
Deine App ist bereit zum Starten
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).
Das startet den Vue/Vite-Development-Server und FastAPI mit Reload-UnterstĂŒtzung. Die generierte Vite-Integration leitet Backend-Anfragen per Proxy an den Python-Development-Server weiter. Dein Browser verbindet sich mit Vite.
So bekomme ich wĂ€hrend der Entwicklung trotzdem die Dinge, die mir am JavaScript-Ăkosystem wirklich gefallen.
Wenn die Entwicklung abgeschlossen ist:
uv build # Installs and (re)builds everything
my-app # CLI entry point provided (in .venv)
Vue wird kompiliert, das Ergebnis wird in die Python-Distribution eingebettet, und die Build-Machinerie hat ihre Mission erfĂŒllt. Du kannst dein Paket mit uv publish veröffentlichen, wenn du möchtest, oder es einfach aus dist/ auf die Produktionsumgebung kopieren, dann installieren und ausfĂŒhren mit:
uv tool install my-app-0.1.0.tar.gz
my-app
Ich musste vorher nur UV und auf dem Entwicklungssystem Node selbst installieren. npm musste ich gar nicht anfassen, und jetzt kann ich die Anwendung anderswo installieren oder ausfĂŒhren, ohne die JavaScript-Werkstatt mitzuschleppen.
Eine Anwendung, endlich
Was ich letztendlich wollte, war nicht besonders exotisch.
- Ich wollte das Backend in Python schreiben.
- Ich wollte das Frontend in Vue schreiben.
- Ich wollte Vite wÀhrend der Entwicklung nutzen.
Und ich wollte eine Sache schnell einrichten und deployen.
Keine Python-Anwendung plus einer JavaScript-Anwendung. Keine zwei Container, die durch nginx und gegenseitiges Misstrauen zusammengehalten werden.
Ein <1MB Python-Paket, das einfach funktioniertâą.
Es enthĂ€lt das Frontend. FastAPI stellt es vom Site-Root aus bereit, ohne den Rest der Anwendung zu verschlucken. Vue-Routing funktioniert, wenn ich eine SPA will, und einfaches File-Routing erlaubt es meinem Backend, Catch-alls fĂŒr sich zu behalten, z. B. um jede hĂŒbsche URL mit serverseitig gerendertem Inhalt zu bedienen wie auf dieser Seite. Das wiederum kann Vue dort einsetzen, wo es gebraucht wird â zwei Welten in perfekter Harmonie.
Die JavaScript-Toolchain tut, was eine Toolchain tun soll:
sie baut die Software und tritt dann beiseite.
Als NĂ€chstes braucht dein Full Stack eine Datenbank â Kanta hat dich abgedeckt. Mit dieser Toolchain lĂ€uft deine App im Handumdrehen.