UNA GUIDA PRATICA COMPLETA
In questa guida imparerai come creare un pacchetto di un semplice TypeScript Reagire all'applicazione in un Pacchetto Python e servilo dal tuo API veloce Server web Python. Dai un'occhiata a cliente e il server repository, se vuoi vedere il codice completo. Iniziamo!
Durante il processo di sviluppo, probabilmente utilizzi due IDE diversi:
- Finestra TypeScript o JavaScript React App, in esecuzione su una porta di ascolto dedicata (ad esempio, 5173) per servire le pagine client/frontend.
- Python FastAPI, in esecuzione su una porta diversa (ad esempio, 8080) per servire un'API REST.
In altre parole, hai due server diversi in esecuzione localmente. Ogni volta che desideri chiamare il tuo server FastAPI, il browser interagisce con due server diversi.
Anche se funziona bene localmente (in localhost
), riscontrerai un errore “Richiesta multiorigine bloccata” nel tuo browser quando distribuisci quel codice. Prima di portare il codice in produzione, la procedura migliore è quella di servire sia le pagine client che l'API REST dallo stesso server Web backend. In questo modo il browser interagirà con un singolo backend. È migliore per sicurezza, prestazioni e semplicità.
1. Crea un'applicazione Simple React
Innanzitutto, nel tuo workspace
directory, creiamo una nuova applicazione TypeScript React utilizzando velocemente:
~/workspace ➜ npm create vite@latest
✔ Project name: … vite-project
✔ Select a framework: › React
✔ Select a variant: › TypeScript
Quindi, accedi alla nuova directory del progetto, installa le dipendenze ed esegui l'applicazione (http://localhost:5173):
~/workspace ➜ cd vite-project
~/workspace/vite-project ➜ npm install
~/workspace/vite-project ➜ npm run dev
Dovresti vedere qualcosa come:
Ora facciamo una piccola aggiunta al modello: aggiungeremo una chiamata HTTP asincrona al futuro backend FastAPI per ottenere il suo stato:
function App() {
...
const (health, setHealth) = useState('');useEffect(() => {
const getStatus = async () => {
const response = await fetch('/v1/health-check/liveness', {
method: 'GET',
});
let status: { (status: string): string } = {};
try {
status = await response.json();
} catch (err) {
console.log(`failed to get backend status. ${err}`);
}
setHealth(status('status') || 'unknown');
};
getStatus();
}, ());
return (
...
<div>Backend Status: {health}</div>
...
)
}
E ora dovremmo ottenere qualcosa del genere:
A questo punto, il Stato del back-end È unknown
perché non l'abbiamo ancora implementato. Non preoccuparti, ce ne occuperemo a breve. Infine, creiamo il client per impacchettarlo in seguito:
~/workspace/vite-project ➜ npm run build
L'output di compilazione dovrebbe creare un file dist
cartella con il codice ottimizzato finale simile a questo:
└── dist/
├── assets/
├── static/
└── index.html
2. Costruire un pacchetto Python
A questo punto passiamo a Python. Preferisco lavorare in a ambiente virtuale per l'isolamento. In un ambiente virtuale dedicato, installeremo twine
E build
per creare il nostro pacchetto Python:
~/workspace/vite-project ➜ python3 -m venv venv
~/workspace/vite-project ➜ . venv/bin/activate
~/workspace/vite-project (venv) ➜ python -m pip install --upgrade pip
~/workspace/vite-project (venv) ➜ pip install twine==5.0.0 build==1.2.1
Creiamone uno nuovo setup.py
file nella cartella principale (vite-project
), con il seguente contenuto:
from setuptools import setup
from pathlib import Pathcwd = Path(__file__).parent
long_description = (cwd / "README.md").read_text()
setup(
name="vite-project",
version="0.0.1",
package_dir={"vite_project": "dist"},
package_data={"vite_project": ("**/*.*")},
long_description=long_description,
long_description_content_type="text/markdown",
)
ed esegui quanto segue per creare il pacchetto:
~/workspace/vite-project (venv) ➜ python setup.py sdist -d tmp
~/workspace/vite-project (venv) ➜ python -m build --wheel --outdir tmp
~/workspace/vite-project (venv) ➜ twine upload -u ${USERNAME} -p ${PASSWORD} --repository-url ${REPO_URL} tmp/*
L'ultima riga sopra è facoltativa se intendi caricare il tuo pacchetto su un repository remoto come PyPI, JFrog Artifabbricaeccetera.
3. Creare un server Web Python FastAPI
Il passaggio finale consiste nel creare il server Python e utilizzare il pacchetto client. Per questo:
- Creane uno nuovo
backend
directory. - Creare un nuovo ambiente virtuale.
- Installa i pacchetti pertinenti e il nostro pacchetto client:
~/workspace/backend ➜ python3 -m venv venv
~/workspace/backend ➜ . venv/bin/activate
~/workspace/backend (venv) ➜ python -m pip install --upgrade pip
~/workspace/backend (venv) ➜ pip install fastapi==0.110.0 uvicorn==0.29.0
~/workspace/backend (venv) ➜ pip install ~/workspace/vite-project/tmp/vite-project-0.0.1.tar.gz
Tieni presente che abbiamo installato il nostro pacchetto client da un percorso locale creato in precedenza. Se hai caricato il tuo pacchetto su un repository remoto, puoi installarlo con:
~/workspace/backend (venv) ➜ pip install --extra-index-url https://${USERNAME}:${PASSWORD}@${REPO_URL} vite-project==0.0.1
Successivamente, creiamo un semplice server Python (2 file):
__main__.py
from distutils.sysconfig import get_python_lib
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from backend.health_router import router
from uvicorn import rundef create_app():
app = FastAPI(
title="Backend Server",
)
app.include_router(router)
client_path = f"{get_python_lib()}/vite_project"
app.mount("/assets", StaticFiles(directory=f"{client_path}/assets"), name="assets")
app.mount("/static", StaticFiles(directory=f"{client_path}/static"), name="static")
@app.get("/{catchall:path}")
async def serve_react_app(catchall: str):
return FileResponse(f"{client_path}/index.html")
return app
def main():
app = create_app()
run(app, host="0.0.0.0", port=8080)
if __name__ == "__main__":
main()
salute_router.py
from typing import Literal
from typing_extensions import TypedDict
from fastapi import APIRouter, statusSTATUS = Literal("success", "error", "partial", "unknown")
class ReturnHealthcheckStruct(TypedDict):
status: STATUS
router = APIRouter(
prefix="/v1/health-check",
tags=("Health Check"),
)
@router.get(
"/liveness",
summary="Perform a Liveness Health Check",
response_description="Return HTTP Status Code 200 (OK)",
status_code=status.HTTP_200_OK,
response_model=ReturnHealthcheckStruct,
)
async def liveness() -> ReturnHealthcheckStruct:
return {"status": "success"}
Nell'implementazione di cui sopra, abbiamo aggiunto il supporto per servire qualsiasi file statico dalla nostra applicazione client montando il file static
E assets
cartelle, così come qualsiasi altro file client che sarà servito dal nostro server Python.
Abbiamo anche creato un semplice endpoint GET, v1/health-check/liveness
che restituisce un semplice {“status": “success"}
Risposta JSON. In questo modo possiamo garantire che il nostro server gestisca sia i file statici del client che la nostra API RESTful lato server.
Ora, se andiamo a host locale:8080 possiamo vedere il nostro client attivo e funzionante. Presta attenzione a Stato del back-end sotto, è adesso success
(piuttosto che unknown
).
In questo tutorial, abbiamo creato una semplice applicazione React che esegue una singola chiamata al backend. Abbiamo racchiuso questa applicazione client come pacchetto Python e l'abbiamo servita dal nostro server web FastAPI Python.
L'utilizzo di questo approccio ti consente di sfruttare i migliori strumenti in entrambi i mondi: TypeScript e React per il frontend e Python con FastAPI per il backend. Tuttavia, vogliamo mantenere un’elevata coesione e un basso accoppiamento tra queste due componenti. In questo modo otterrai tutti i vantaggi:
- Velocity, separando front-end e back-end in repository diversi, ogni parte può essere sviluppata da un team diverso.
- Stabilità e qualità, bloccando un pacchetto client con versione e aggiornandolo solo quando il server è pronto a supportare una nuova versione client.
- Sicurezza: il browser interagisce con un solo server backend. Non è necessario abilitare CORS o altre soluzioni alternative che compromettano la sicurezza.
- Semplicità: lavorando tramite un unico server
Fonte: towardsdatascience.com