Vue-Python

FastAPI menee Full Stackiin

Tarinan siitä, kuinka Vue näki käärmeen ja rakastui.

FastAPI on yksi niistä ohjelmistoista, jotka saavat minut epäileväiseksi, koska Pythonin backend-tarina on liian miellyttävä. Tyyppivihjeet Flask/Express-tyyppisissä handlereissa muuttuvat parsinnaksi ja validoinniksi. Async toimii auto-reloadeineen ilman, että tarvitsee manailla esiin mitään järjettömän monimutkaista framework-konfiguraatiokäsikirjaa. Kun siihen heitetään vielä OpenAPI-skeemat ja auto-reloadit, pääset git init:stä kunnolliseen API:in ennen kuin kahvinkeitin on ehtinyt käynnistyä.

Sitten tarvitset frontin.

Historiallisesti tässä neonvalaistu moottoritie päättyi äkisti betoniseinään.

FastAPI on erittäin hyvä API:jen tekemisessä, mutta se ei oikeastaan pysty tarjoilemaan fronttiasi sivuston juuressa, ellet halua kaikkien pyyntöjäsi reititettävän StaticFiles:iin. Ja SPA-tuki on täysin tuntematon käsite. Voisit kyllä tarjoilla tiedostoja, renderöidä templateja tai liittää mitä tahansa koneistoa halusit, mutta viimeinen maili modernin JavaScript-frontin ja Python-sovelluksen välillä jätettiin suurelta osin lukijan itsensä ratkaistavaksi.

Ja tuo lukija olin yhä useammin minä.

Sillä aikaa frontend-maailmassa

Käytän tähän hommaan Vue:ta. Vue toimii suunnilleen samalla areenalla Reactin ja Svelten kanssa: komponenttipohjaiset käyttöliittymät, reaktiivinen tila, client-side-reititys ja tavanomainen moderni kalusto, jolla kasa lähdekooditiedostoja muutetaan joksikin, mitä selain voi suorittaa.

fastapi-vue-pakettia voi kuitenkin käyttää myös muiden kanssa, joten kerron lyhyesti kilpailijoista.

React

React on alan 360-kiloinen gorilla. Sillä on valtava ekosysteemi, vastaus kaikkeen, ja yleensä vielä kolme muuta vastausta, jotka kinastelevat ensimmäisen kanssa.

Sen suurin käytännön etu on yksinkertaisesti vetovoima: monet kehittäjät osaavat sitä, monet kirjastot on tehty sitä varten, ja moni esimerkkikoodi alkaa komennolla npm install react. Se on valtavan paljon suositumpi kuin Vue, mikä tekee Reactin ja FastAPIn varsin ohuesta integraatiosta jo itsessään melko paljonpuhuvaa.

Jopa virallinen FastAPI:n full-stack-ratkaisu — julkaistu vasta muutama viikko sitten — on pohjimmiltaan template-repositorio: valmiiksi valittu React-stack, josta lähdetään liikkeelle, sen sijaan että kyseessä olisi työkalu, joka osaisi astella olemassa olevaan Python-sovellukseen ja liittää siihen frontin.

Svelte

Svelte nojaa enemmän compileriin. Sen sijaan että se toimittaisi yhtä paljon framework-koneistoa selaimeen, se muuntaa komponentit build-ajankohtana. Lopputulos voi silti pyöriä tuotannossa ilman Nodea.

Oikea reaktiivisuus vaatii hieman temppuilua: sen omassa kielessä täytyy käyttää $-merkkiä ilmaisemaan, mikä tarvitsee päivityksen. Toisaalta tämä oma kieli mahdollistaa kilpailijoita lyhyemmän koodin.

Vue

Vue sijoittuu mieluiseen välimuotoon: tarpeeksi rakennetta isojenkin sovellusten rakentamiseen, mutta ilman että HTML katoaa useiden JavaScript-kerrosten alle. Reaktiivisuus Pinia-storeineen vain toimii ja pysyy poissa tieltä. Ja kun tarvitset sitä viimeistäkin suorituskyvyn pisaraa, se integroituu hyvin ei-reaktiivisiin rakenteisiin. Ei monimutkaista käärimistä eikä useState-tyylistä boilerplatea tarvita.

Ja se toimii nopeasti, mikä ei ole totta useimmista React-sovelluksista.

Sen virallinen create-vue-työkalu antaa myös jotain erityisen hyödyllistä tässä: minun ei tarvitse keksiä omaa käsitystäni siitä, miltä Vue-projektin pitäisi näyttää. Se voi vuorovaikutteisesti kysyä, pitäisikö sovelluksessa käyttää TypeScriptiä, Vue Routeria, Piniaa, Vitestiä, Playwright/Cypressiä, ESLintia, Prettieria ja muuta tavallista varustusta.

Siitä tulee tärkeää myöhemmin.

Vue-Svelte-React
Ratkaistaan sovelluksissa usein esiintyvä minimaalinen verkkolomake: pudotusvalikko muokattavan tietueen valitsemiseen sekä syöttökentät sen kentille. Svelten koodi on lyhyin, mutta Vue on tavallinen selaimen index.html ja todellisuudessa yhtä pitkä. Ja sitten tulee React, johon kuluu liikaa työtä mahtuakseen yhteen kuvakaappaukseen. Kaikki työpaikat ovat Reactissa, ja tämä kuva näyttää miksi.

Miten tämä otetaan oikeasti käyttöön?

Kun minulla on toisella puolella FastAPI ja toisella Vue, React tai Svelte, on olemassa muutama pohjimmiltaan erilainen tapa, miten voin saada syntyvän otuksen palvelimelle.

Vaihtoehto 1: antaudu Nodelle

Ilmeinen JavaScript-maailman vastaus on laittaa JavaScript-palvelin tuotantoon myös.

Tämä on täysin järkevää, jos todella haluan Node-backendin. Yhden kielen ja läheisesti toisiinsa liittyvän työkaluston käyttämisessä langan molemmilla puolilla on aito etu. Jos tiimini haluaa TypeScriptin kaikkialle, se on täysin johdonmukainen arkkitehtuuri.

En halua.

Valitsin FastAPI:n, koska haluan kirjoittaa backendin Pythonilla. Toisen ajonaikaisen asentaminen sen rinnalle vain frontti-assettien toimittamiseksi tuntuu siltä kuin palkkaisi toisen kokin kantamaan lautasia keittiöstä ruokasaliin.

image
Haluanko gigatavuja node_modules-kansioita ja tämän tuotantopalvelimelleni? En.

Lisäksi on pieni asia nimeltä JavaScript-riippuvuuksien universumi. Se on liian raskas, yhteensopivuus on heikkoa ja se on usein rikki. Vaikka nvm osaa helposti asentaa oikean Node-version, se vie silti valtavasti tilaa, jota hostatuilta palvelimilta usein puuttuu.

Node on hyödyllinen kehityskoneellani. Vite on erinomainen. create-vue on erinomainen. Build-ekosysteemi hoitaa tehtävänsä erittäin hyvin.

Se ei tarkoita, että haluaisin mitään siitä tuotantokoneelle.

Vaihtoehto 2: renderöi kaikki palvelimella

Toisessa ääripäässä voin jättää SPA:n kokonaan väliin ja generoida HTML:n Pythonilla.

Tämä ansaitsee enemmän arvostusta kuin joskus saa. Jokainen sovellus ei tarvitse asiakaspuolen sovellusajonaikaa, reititintä ja useita megatavuja siviilisaation tukirakenteita.

Työkalut kuten htmx vievät palvelimella renderöidyn HTML:n yllättävän pitkälle lisäämällä HTTP-pyyntöjä, swappeja, transitioita, WebSocketteja ja server-sent eventsejä suoraan HTML-attribuuttien kautta. Yhdistä tämä html5taggeriin, jolla luot kyseiset dokumentit ja HTML-fragmentit palvelimella, niin saat sivuston, joka toimii kaikkialla ja jota voivat lukea niin some, hakukoneet kuin agentitkin.

Sivustoille, joita hallitsevat dokumentit, lomakkeet ja suhteellisen yksinkertaiset interaktiot, pidän tästä arkkitehtuurista. Selain pyytää jotain, Python tuottaa HTML:n, eikä kukaan tarvitse build-pipelinea, jolla voisi laukaista Mars-luotaimen.

Vasanko.com on rakennettu tällä menetelmällä, ja Vue on vain pultattu mukaan admin-käyttöliittymään, jossa tarvitaan vahvempaa interaktiivisuutta. Tämä CMS pyörii FastAPI–Vue-kokoonpanolla, mutta renderöi kaikki sivut palvelimella Pythonilla.

Vaihtoehto 3: aja kahta palvelinta

Yleinen kompromissi on siis:

  • FastAPI ajaa API:a.
  • Vite tai jokin muu JavaScript-palvelin ajaa fronttia
  • Reverse proxy laittaa molemmat saman hostnamen alle.
  • Tuotannossa on nyt kaksi sovellusta, kaksi riippuvuuspinoa, kaksi prosessia ja vielä yksi konfiguraatiotiedosto pitämässä liittoa koossa.

Tämä toimii, mutta ei ole kovin kaunista.

Selain ei välitä, onko app.js:n tuottanut Python, Node, Caddy, nginx vai riittävän päättäväinen leivänpaahdin. Kun frontti on rakennettu, tuloksena on staattista HTML:ää, CSS:ää, JavaScriptiä, fontteja ja kuvia.

Miksi siis pitää tehdas käynnissä, kun tuote on jo poistunut kokoonpanolinjalta?

Rakenna JavaScriptillä, aja Pythonilla

Siitä tuli keskeinen idea fastapi-vue-setup:n taustalla.

Haluan JavaScript-työkalut sinne, missä niistä on todella hyötyä:

  • create-vue frontin luomiseen
  • Vite kehityspalvelimena
  • Välittömät hot-reloadit muutoksista
  • Normaali Vue-build Nodella, Bunilla tai Denolla

Ja sitten haluan sen poissa.

Tuotantoartefaktin tulisi olla Python-paketti, joka sisältää valmiiksi buildatun frontin. Kyseisen paketin asentaminen ei saisi vaatia Nodea, npm:ää, Viteä, Vue-lähdekoodia tai sitä painovoima-anomaliaa, joka perinteisesti sijaitsee hakemistossa node_modules.

Juuri näin fastapi-vue-setup buildaa projektit nykyään: uv build ajaa frontin buildauksen ja sisällyttää tuloksena syntyvät assetit Python-paketin sisään. Generoitu Hatch-konfiguraatio käsittelee frontend-build-vaihetta artefaktina ja kytkeytyy sdist-buildaukseen, samalla kun paketin sisältö rajataan pelkkään Python-pakettiin.

Ero on merkityksellinen erityisesti sdistin kohdalla. En halua, että lähdejakelu tarkoittaa “tässä on vähän Pythonia, tässä on kokonainen Vue-kehityspuu, asenna nyt Node ja rekonstruoi varsinainen sovellus itse”. Frontti on jo käännetty ennen kuin jakelupaketti lähtee koneeltani.

Paketti sisältää sen, mitä ajamiseen tarvitaan — ei kaikkea, mitä satuin tarvitsemaan sen rakentamiseen.

Tämän jälkeen julkaisu näyttää ihastuttavan tylsältä:

uvx my-app

Ei Node-asennusseremoniaa kohdekoneella. Ei npm install-komentoa. Ei fronttipalvelinta. Ei JavaScript-riippuvuuspuuta, joka herää kolmen kuukauden kuluttua pyytämään ruokaa.

Paketti käynnistää FastAPIn, ja FastAPI tarjoilee sovelluksen.

Toinen ruma ongelma

Frontin rakentaminen oli vasta puoli matkaa.

StaticFiles ei ole aivan frontend-palvelin

FastAPI tarjoaa Starletten StaticFiles-luokan, ja tavallisille staattisille tiedostoille se tekee juuri sen, mitä lupaa:

app.mount("/static", StaticFiles(directory="static"))

Ongelmat alkavat, kun frontend on itse sivusto.

Vue-sovellus haluaa normaalisti polun /. Se voi haluta myös /login, /settings, /dashboard/coffee-reactor/7 ja mitä tahansa muuta, joka kuuluu client-sivun routerille.

Mutta StaticFiles-komponentin mounttaaminen polkuun / käytännössä antaa kyseiselle mountatulle sovellukselle koko jäljellä olevan URL-avaruuden haltuun. FastAPI:n oma ylläpitäjä selitti ongelman jo vuosia sitten: mounttaa se juureen, niin staattinen sovellus ottaa vallan, jolloin sen alla olevat tavalliset path-operaatiot eivät enää toimi odotetulla tavalla.

Ja tarvitsemme:

  • Frontend-build kohteessa /*
  • FastAPI hoitaa edelleen
    • /api/...
    • /openapi.json
    • kaikki muut vapaavalintaiset reitit

Tämä ei ole sama ongelma kuin tiedoston /static/logo.svg tarjoaminen.

FastAPI on hyvin äskettäin parantanut omaa frontti-tukeaan, ja nykyinen dokumentaatio ohjaa nyt nimenomaisesti frontend-sovellukset käyttämään app.frontend()-metodia pelkän StaticFiles-luokan sijaan. Mutta fastapi-vue on tätä ratkaisua vanhempi, ja sen tehtävä on myös jonkin verran erilainen: se on tämän kokonaisen paketointijärjestelmän pieni runtime-puolisko.

Fastapi-vue-runtime

Kumppanipaketti fastapi-vue tarjoaa oman Frontend-käsittelijän sen sijaan, että sovellukseen liitettäisiin StaticFiles.

SPA-tilassa se voi palauttaa index.html-tiedoston sellaisille poluille, jotka kuuluvat asiakaspuolen reitittimelle, sen sijaan että se vaatisi fyysisen tiedoston kyseisellä nimellä.

Note

FastAPI-reitityksen toimintatavan vuoksi app-mountit syövät kaiken kyseisen polun alta. Loput reiteistä kokeillaan järjestyksessä ja ensimmäinen osuma voittaa. Siksi SPA-tilassa catch-all-reitti on sijoitettava viimeiseksi app-moduuliin.

Kun SPA-tila on pois käytöstä, se sitoutuu vain todellisten tiedostojen polkuihin, jolloin sen jälkeen tulevat reitit voivat edelleen napata kaiken, mikä putoaa läpi.

Se hoitaa myös vähemmän näyttävät yksityiskohdat, joita en erityisesti haluaisi toteuttaa uudelleen joka tiistai:

  • ETag ja Last-Modified
  • Muuttumaton välimuisti buildatuille asseteille
  • RAM-välimuisti zstd-pakkauksella
  • SPA-reitityksen fallback ja /favicon.ico tarvittaessa
  • Estää vahingossa vanhentuneen buildin tarjoilun dev-tilassa

Tämä on tuotantoajon ajonaikainen riippuvuus. Se on pieni ja pelkkää Pythonia.

Kaikki Vue-sovelluksen buildaamiseen ja kehittämiseen tarvittava kalusto pysyy lähdeprojektissa.

Esittelyssä fastapi-vue-setup

Kun nämä palaset ovat paikoillaan, fastapi-vue-setup keskittyy pääasiassa poistamaan toistuvan kytkentätyön.

Peruskomento on tarkoituksella vaikuttamaton: osoita se sovelluskansioosi, tai . jos olet jo siellä.

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.

Mutta sen taakse kätkeytyy kaksi melko erilaista tapausta.

Ja se tarjoaa apuvälineitä sovelluksesi CLI-käynnistyspisteelle, joka voi siten ottaa vastaan myös omia komentorivivalintojasi — jotakin, mitä fastapi run ei pysty tarjoamaan.

Uuden sovelluksen luominen

Uuden projektin kohdalla en halua, että fastapi-vue-setup pakottaa käyttöön valmiiksi jäädytetyn, itse tekemäni Vue-templaten. Haluan, että se luo Python-projektin, joka on konfiguroitu antamallani nimellä ja valitsemallani Vue-setupilla:

  • JavaScript tai TypeScript
  • Vue Router tai ei routeria
  • Pinia tai ei
  • Testaus-, linttaus- ja formatointivalinnat
  • Muut nykyisen create-vue:n tukemat vaihtoehdot

Työkalu rakentaa sitten FastAPI-integraation sen sovelluksen ympärille, jonka kehittäjä todella valitsi.

Tämä on tärkeää, koska templaatti fossiloituu väistämättä jonkun mieltymyksiksi. Puolen vuoden kuluttua sen käsitys modernista full stackista saattaa jo kuulua museonäyttelyyn, ja sinä olet jumissa sen kanssa, mitä sait.

Mieluummin minulla on valinnanvaraa.

Asetukset valmiit

Onko sinulla jo sovellus?

Tässä minulla saattaa jo olla oikea projekti, joka sisältää koodia, jota en haluaisi haihduttaa. Sen voi luoda samalla skriptillä tai se voi olla itsenäinen, ja skripti paikkaa sen tarvittaessa.

Asennusskripti havaitsee Python-projektin ja backend-moduulin, tunnistaa tai luo Vue-frontin ja paikkaa ne osat, jotka se voi turvallisesti integroida mahdollisimman pienin muutoksin.

Note

Siirrä olemassa oleva Vuesi ensin kokonaan frontend/-hakemiston alle. Sijoitamme Vuen sinne, jotta juurihakemisto ei täyttyisi Node-tavaroista. Laita se sinne, jotta skripti päivittää olemassa olevan sovelluksen uuden luomisen sijaan.

Täytyykö päivittää uusimpaan versioon? Aja vain fastapi-vue-setup uudelleen, niin se paikkaa mukaan uudet ominaisuudet, joita siihen on tullut.

Tämä on yksi merkittävimmistä eroista tämän ja template-repositorion välillä.

Templaatti sanoo:
Forkkaa tämä repositorio aloittaaksesi projektisi

Minä tarvitsin työkaluja, jotka osaavat myös sanoa:
Selvä, olet jo 30 000 riviä sisällä. Näytä minulle, missä potilas on.

Sovelluksesi on valmis ajettavaksi

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).

Tämä käynnistää Vue/Vite-kehityspalvelimen ja FastAPI:n uudelleenlataustuella. Generoitu Vite-integraatio välittää backend-pyynnöt Python-kehityspalvelimelle. Selaimesi yhdistää Viteen.

Joten kehityksen aikana saan edelleen ne asiat, joista oikeasti pidän JavaScript-ekosysteemissä.

Hello world yhdistää FastAPI:in

Kun kehitys on valmis:

uv build  # Installs and (re)builds everything
my-app    # CLI entry point provided (in .venv)

Vue on käännetty, tulos on leivottu Python-distribuution sisään ja build-koneisto on saanut tehtävänsä valmiiksi. Voit halutessasi julkaista pakettisi komennolla uv publish, tai vain kopioida dist/-kansiosta suoraan tuotantoon, ja sitten asentaa ja ajaa seuraavasti:

uv tool install my-app-0.1.0.tar.gz
my-app

Minun tarvitsi asentaa etukäteen vain UV ja kehitysjärjestelmälle Node itsessään. Minun ei tarvinnut koskea npm:iin lainkaan, ja nyt voin asentaa tai ajaa sovellusta muuallakin ilman, että JavaScript-verstas pitää raahata mukana.

Yksi sovellus, vihdoin

Se mitä lopulta halusin ei ollut erityisen eksoottista.

  • Halusin kirjoittaa backendin Pythonilla.
  • Halusin kirjoittaa frontin Vuella.
  • Halusin käyttää Viteä kehityksen aikana.

Ja halusin setupata ja deployata yhden asian nopeasti.

Ei Python-sovellusta plus JavaScript-sovellusta. Ei kahta konttia, joita yhdistää nginx ja molemminpuolinen epäluulo.

Alle 1 MB:n Python-paketti, joka Toimii Suoraan™.

Se sisältää frontendin. FastAPI tarjoilee sen sivuston juuresta nielemättä mukanaan muuta sovellusta. Vuen reititys toimii, kun haluan SPA:n, ja pelkkä tiedostoreititys antaa backendini pitää catch-allit itsellään — esimerkiksi käsitellä mitä tahansa siistiä URL-osoitetta palvelinpuolella renderöidyllä sisällöllä, kuten tällä sivustolla. Tämä puolestaan voi käyttää Vuea paikoissa, joissa sitä tarvitaan — kaksi maailmaa täydellisessä harmoniassa.

JavaScript-työkaluketju tekee sen, mitä työkaluketjun kuuluukin tehdä:

se kääntää ohjelmiston ja siirtyy sitten pois tieltä.

Seuraavaksi full stackisi tarvitsee tietokannan, ja Kanta hoitaa homman. Tällä työkaluketjulla saat sovelluksesi pyörimään hetkessä.