En FastAPI, un endpoint es una ruta de la API que responde a una petición HTTP. El método más usado es GET, que se utiliza para obtener datos. Cada endpoint se define con un decorador (@app.get()
) y una función asociada.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(
title="API de Productos",
version="0.1.0",
description="Ejemplo inicial con endpoint GET en FastAPI"
)
# ---------- Modelo de datos ----------
class Producto(BaseModel):
codigo: int
descripcion: str
precio: float
# ---------- "Base de datos" simulada ----------
productos_db = [
{"codigo": 1, "descripcion": "Teclado", "precio": 50.0},
{"codigo": 2, "descripcion": "Mouse", "precio": 30.0},
{"codigo": 3, "descripcion": "Monitor", "precio": 200.0},
]
# ---------- Endpoint GET raíz ----------
@app.get("/")
def leer_inicio():
return {"mensaje": "Bienvenido a mi primera API con FastAPI"}
# ---------- Endpoint GET con parámetro ----------
@app.get("/productos/{codigo}")
def leer_producto(codigo: int, consulta: str | None = None):
for prod in productos_db:
if prod["codigo"] == codigo:
return {"producto": prod, "consulta": consulta}
raise HTTPException(status_code=404, detail="Producto no encontrado")
class Producto(BaseModel)
: modelo que define la estructura de un producto (codigo
, descripcion
, precio
).productos_db
: lista en memoria que simula una base de datos.@app.get("/")
: endpoint raíz de la API, devuelve un mensaje de bienvenida.@app.get("/productos/{codigo}")
: endpoint que recibe un código de producto como parámetro en la URL.consulta
. Si no lo encuentra, responde con 404.Ejecutar el servidor:
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Abrí tu navegador en las siguientes rutas:
Raíz de la API
http://127.0.0.1:8000/
Respuesta:
{"mensaje":"Bienvenido a mi primera API con FastAPI"}
Consultar un producto existente
http://127.0.0.1:8000/productos/2
Respuesta:
{"producto":{"codigo":2,"descripcion":"Mouse","precio":30.0},"consulta":null}
Consultar un producto con parámetro de query
http://127.0.0.1:8000/productos/2?consulta=oferta
Respuesta:
{"producto":{"codigo":2,"descripcion":"Mouse","precio":30.0},"consulta":"oferta"}
Consultar un producto inexistente
http://127.0.0.1:8000/productos/99
Respuesta:
{"detail":"Producto no encontrado"}
FastAPI genera documentación interactiva automáticamente:
http://127.0.0.1:8000/docs # Swagger UI
http://127.0.0.1:8000/redoc # ReDoc
Desde allí podés probar el endpoint GET directamente en el navegador sin escribir código extra.
/productos/{codigo}
) y en la query (?consulta=valor
).from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import random
app = FastAPI(
title="API de Chistes",
version="1.0.0",
description="API + página HTML servidas desde el mismo origen"
)
# Lista de chistes
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."
]
# ---------------------- Endpoints API ----------------------
# Raíz -> devuelve todos los chistes
@app.get("/")
def obtener_todos():
return {"chistes": chistes}
# /chiste -> devuelve un chiste aleatorio
@app.get("/chiste")
def obtener_chiste():
return {"chiste": random.choice(chistes)}
# ---------------------- Página HTML (cliente) ----------------------
@app.get("/pagina", response_class=HTMLResponse)
def pagina():
html = '''<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<title>Chistes · Demo Fetch</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body { font-family: system-ui, Arial, sans-serif; margin: 24px; }
h1, h2 { margin: 0 0 12px; }
table { border-collapse: collapse; width: 100%; margin-top: 8px; }
th, td { border: 1px solid #ccc; padding: 8px; vertical-align: top; }
th { background: #f6f6f6; text-align: left; }
.panel { border: 1px solid #e0e0e0; padding: 16px; border-radius: 8px; margin: 20px 0; }
.btn { padding: 8px 12px; cursor: pointer; border: 1px solid #222; background: #fff; border-radius: 6px; }
.btn:hover { background:#f3f3f3; }
#estado { color: #666; font-size: 0.95rem; }
#chiste-aleatorio { font-size: 1.1rem; margin-top: 10px; }
</style>
</head>
<body>
<h1>API de Chistes · Cliente mínimo</h1>
<div class="panel">
<h2>Todos los chistes</h2>
<div id="estado">Cargando chistes</div>
<table id="tabla-chistes" aria-label="Tabla de chistes" style="display:none">
<thead>
<tr>
<th>#</th>
<th>Chiste</th>
</tr>
</thead>
<tbody id="cuerpo-tabla"></tbody>
</table>
</div>
<div class="panel">
<h2>Chiste aleatorio</h2>
<button class="btn" id="btn-aleatorio" type="button">Obtener chiste aleatorio</button>
<div id="chiste-aleatorio" role="status" aria-live="polite"></div>
</div>
<script>
const BASE_URL = ""; // misma app/puerto
async function cargarTodosLosChistes() {
const estado = document.getElementById("estado");
const tabla = document.getElementById("tabla-chistes");
const cuerpo = document.getElementById("cuerpo-tabla");
estado.textContent = "Cargando chistes";
try {
const res = await fetch(`${BASE_URL}/`);
if (!res.ok) throw new Error(`Error HTTP ${res.status}`);
const data = await res.json();
cuerpo.innerHTML = "";
(data.chistes || []).forEach((chiste, idx) => {
const tr = document.createElement("tr");
const tdIdx = document.createElement("td");
const tdTxt = document.createElement("td");
tdIdx.textContent = String(idx + 1);
tdTxt.textContent = chiste;
tr.appendChild(tdIdx);
tr.appendChild(tdTxt);
cuerpo.appendChild(tr);
});
tabla.style.display = "table";
estado.textContent = `${(data.chistes || []).length} chistes cargados.`;
} catch (err) {
estado.textContent = "No se pudieron cargar los chistes.";
console.error(err);
}
}
async function cargarChisteAleatorio() {
const destino = document.getElementById("chiste-aleatorio");
destino.textContent = "Buscando chiste";
try {
const res = await fetch(`${BASE_URL}/chiste`);
if (!res.ok) throw new Error(`Error HTTP ${res.status}`);
const data = await res.json();
destino.textContent = data.chiste ?? "Sin chiste disponible.";
} catch (err) {
destino.textContent = "No se pudo obtener un chiste ahora.";
console.error(err);
}
}
document.addEventListener("DOMContentLoaded", () => {
cargarTodosLosChistes();
cargarChisteAleatorio();
document.getElementById("btn-aleatorio").addEventListener("click", cargarChisteAleatorio);
});
</script>
</body>