Hasta ahora aprendimos a crear endpoints con GET que devuelven datos en JSON. El siguiente paso es aprender a servir páginas HTML, CSS y JS desde FastAPI, de modo que puedas ver tus APIs funcionando dentro de una página web.
StaticFiles
.fetch
en JavaScript para consumir los endpoints /chiste
y /chistes
de la API de chistes.En main.py
definimos dos endpoints de API y configuramos la raíz para devolver el index.html
.
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from pathlib import Path
import random
app = FastAPI(title="API de Chistes")
BASE_DIR = Path(__file__).resolve().parent
app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static")
chistes = [
"Por qué los programadores confunden Halloween y Navidad? Porque OCT 31 == DEC 25.",
"¡Camarero! Ese filete tiene muchos nervios. Pues normal, es la primera vez que se lo comen.",
"¿Cómo se despiden los químicos? ¡Ácido un placer.",
"Estás obsesionado con la comida, de verdad. ¿Qué dices croquetamente?",
"¿Cuál es el café más peligroso del mundo? El ex-preso."
]
# Raíz -> devuelve la página HTML
@app.get("/", response_class=FileResponse)
def index():
return FileResponse(BASE_DIR / "static" / "index.html")
# API -> todos los chistes
@app.get("/chistes")
def obtener_chistes():
return {"chistes": chistes}
# API -> chiste aleatorio
@app.get("/chiste")
def obtener_chiste():
return {"chiste": random.choice(chistes)}
Ya quedó montada en el código anterior con:
app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static")
Esto hace que cualquier archivo dentro de static/
se pueda acceder como http://127.0.0.1:8000/static/...
Crear static/index.html
con este contenido:
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Chistes con FastAPI</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<h1>API de Chistes Cliente HTML</h1>
<section>
<h2>Todos los chistes</h2>
<div id="lista-chistes">Cargando...</div>
</section>
<section>
<h2>Chiste aleatorio</h2>
<button id="btn-aleatorio">Obtener chiste</button>
<div id="chiste-aleatorio"></div>
</section>
<script src="/static/app.js"></script>
</body>
</html>
Archivo static/style.css
:
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: darkblue; }
section { margin-bottom: 20px; }
#lista-chistes { border: 1px solid #ccc; padding: 10px; }
#chiste-aleatorio { margin-top: 10px; font-style: italic; }
button { padding: 5px 10px; cursor: pointer; }
Archivo static/app.js
:
async function cargarChistes() {
const res = await fetch("/chistes");
const data = await res.json();
const div = document.getElementById("lista-chistes");
div.innerHTML = "";
(data.chistes || []).forEach((chiste, idx) => {
const p = document.createElement("p");
p.textContent = `${idx + 1}. ${chiste}`;
div.appendChild(p);
});
}
async function cargarChisteAleatorio() {
const res = await fetch("/chiste");
const data = await res.json();
document.getElementById("chiste-aleatorio").textContent = data.chiste ?? "";
}
document.getElementById("btn-aleatorio").addEventListener("click", cargarChisteAleatorio);
cargarChistes();
Nuestra aplicación queda con los siguientes archivos:
Si accedemos a la raiz del sitio, se nos retorna la página index.html
Listo. Al abrir http://127.0.0.1:8000/
se carga index.html
, que a su vez hace fetch
a /chistes
y /chiste
para mostrar los datos.