App JS + backend Python: integration guidee
Publié le 2026-04-13
Objectif
L’objectif etait de transformer la page “App JS” en une vraie integration frontend + backend:
- entree utilisateur cote navigateur
- appel HTTP vers le backend Python dans le conteneur API
- reponse exploitable dans l’interface
Le cas d’usage retenu: assistant de titre d’article.
L’utilisateur saisit un titre, puis l’application recupere:
- un
slugpret a utiliser dans un nom de fichier - un nombre de mots
- une estimation du temps de lecture
1) Endpoint Python ajoute
Le backend expose un endpoint dedie:
@app.post("/tools/slugify")
def tool_slugify(payload: dict[str, str]) -> dict[str, str | int]:
text = (payload.get("text") or "").strip()
if not text:
raise HTTPException(status_code=400, detail="text is required")
if len(text) > 300:
raise HTTPException(status_code=400, detail="text must be at most 300 characters")
slug = sanitize_slug(text)
words = count_words(text)
read_minutes = max(1, math.ceil(words / 220))
return {
"input": text,
"slug": slug,
"word_count": words,
"estimated_read_minutes": read_minutes,
}
Points importants:
- validation entree simple et explicite
- logique de calcul courte et lisible
- format de sortie stable pour le frontend
2) CORS active pour la communication cross-domain
Le frontend est servi par DOMAIN_SITE, l’API par DOMAIN_API.
Il faut donc autoriser le navigateur a appeler l’API:
app.add_middleware(
CORSMiddleware,
allow_origins=CORS_ALLOWED_ORIGINS,
allow_credentials=False,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["*"],
)
Variable utile cote infrastructure:
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-*}
3) App JS remplacee dans Astro
La page apps/counter est devenue un formulaire interactif qui appelle l’API:
<input id="titleInput" type="text" maxlength="300" />
<button id="generateBtn">Generer</button>
<code id="slugOut"></code>
Puis appel reseau:
const response = await fetch(`${runtimeApiBaseUrl}/tools/slugify`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text })
});
const payload = await response.json();
Et rendu du resultat:
slugOut.textContent = payload.slug;
wordCountOut.textContent = String(payload.word_count);
readTimeOut.textContent = String(payload.estimated_read_minutes);
4) URL API configurable et fallback runtime
La base URL API est configurable via PUBLIC_API_BASE_URL.
En plus, un fallback runtime derive automatiquement api. depuis site. si la valeur est encore par defaut.
Exemple:
const derivedHost = window.location.hostname.replace(/^site\./, "api.");
return `${window.location.protocol}//${derivedHost}`;
Resultat: integration plus robuste en environnement auto-heberge, meme si la variable n’a pas encore ete personnalisee.
5) Ajustements d’infrastructure
Build args et variables ajoutes:
PUBLIC_API_BASE_URL: "${PUBLIC_API_BASE_URL:-https://api.example.com}"
PUBLIC_API_BASE_URL=https://api.example.com
CORS_ALLOWED_ORIGINS=https://site.example.com
6) Verification rapide
Test backend direct:
curl -sS -X POST "https://${DOMAIN_API}/tools/slugify" \
-H "Content-Type: application/json" \
-d '{"text":"Titre utilisateur pour test backend"}'
Exemple de reponse:
{
"input": "Titre utilisateur pour test backend",
"slug": "titre-utilisateur-pour-test-backend",
"word_count": 5,
"estimated_read_minutes": 1
}
Conclusion
Ce lot montre un pattern simple et durable:
- interface Astro/JS legere
- endpoint Python clair
- configuration explicite au niveau conteneurs
- communication cross-domain geree proprement
Ce type de micro-integration permet d’ajouter des outils utiles sans transformer le CMS en monolithe complexe.