Creación de endpoints con GET

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.

Código básico en main.py

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

Explicación del código

  • 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.
  • Si encuentra el producto, lo devuelve junto con el valor opcional de consulta. Si no lo encuentra, responde con 404.

Probar desde el navegador

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"}

Documentación automática en el navegador

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.

Swagger UI - Probando endpoint GET ReDoc - Vista de documentacion GET

Conclusión

  • Un endpoint GET sirve para consultar información.
  • Puedes recibir parámetros en la ruta (/productos/{codigo}) y en la query (?consulta=valor).
  • FastAPI valida tipos, convierte respuestas a JSON y genera documentación automática.

Otro problema resuelto

API de Chistes con GET (raíz y chiste aleatorio)

1) Código en main.py

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>
'''
    return HTMLResponse(content=html)

2) Explicación del código

  • @app.get("/"): cuando entres a la raíz, devuelve la lista completa de chistes en un JSON.
  • @app.get("/chiste"): devuelve un único chiste seleccionado al azar.
  • @app.get("/pagina", response_class=HTMLResponse): sirve una página HTML mínima que consume la API con fetch desde el mismo origen, luego veremos como servir páginas estáticas.

3) Probar desde el navegador

Levantá el servidor:

uvicorn main:app --reload

Abrí las siguientes rutas en el navegador:

Raíz (todos los chistes)

http://127.0.0.1:8000/

Respuesta:

{
  "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."
  ]
}

Un chiste aleatorio

http://127.0.0.1:8000/chiste

Respuesta (cambia cada vez que recargues):

{"chiste": "¿Cuál es el café más peligroso del mundo? El ex-preso."}

Página HTML (cliente mínimo)

http://127.0.0.1:8000/pagina

Página HTML de chistes consumiendo la API

Problema propuesto

Crear un endpoint GET en FastAPI llamado /pais/{nombre}.

Definir una base de datos simulada (una lista de diccionarios) con al menos 5 países. Cada país debe tener:

  • nombre
  • capital
  • población

Cuando el usuario envíe un país en la URL, el endpoint debe devolver un JSON con su capital y población.

Si el país no existe en la base de datos, devolver un error 404 con el mensaje "País no encontrado".