Elegir espacio

Dos espacios

¿Qué quieres consultar?

Elige el espacio al que quieres entrar.

Biblioteca FastAPIPon en marcha tu primera API

Paso 1 · Bloque II · Unidad 2.1

Pon en marcha tu primera API

Crea y ejecuta una operación GET, comprueba su JSON y OpenAPI y sigue la petición desde Uvicorn hasta la función de ruta.

Tu primera respuesta observable#

Crea main.py y ejecuta primero el corte mínimo. Todavía no necesitas conocer ASGI ni la arquitectura interna para observar el contrato:

from fastapi import FastAPI

app = FastAPI()


@app.get("/health")
def health() -> dict[str, str]:
    return {"status": "ok"}
uv init --bare
uv add "fastapi[standard-no-fastapi-cloud-cli]"
uv run fastapi dev main.py
curl -i http://127.0.0.1:8000/health

La evidencia mínima es 200 OK, un cuerpo {"status":"ok"} y la operación GET /health visible en http://127.0.0.1:8000/docs. Con ese fenómeno ya observable, el resto de la unidad explica qué lo hizo posible y cómo localizar un fallo.

En este bloque ya escribimos FastAPI, pero todavía no construimos una arquitectura completa. La meta es dominar la frontera HTTP de una aplicación pequeña: cómo arranca, cómo encuentra una operación, cómo valida y cómo forma una respuesta. Todo el estado será temporal y vivirá en memoria.

Al terminar deberías poder crear un entorno aislado, explicar main:app, distinguir recarga de desarrollo y ejecución normal, predecir qué ruta coincide y localizar en qué frontera se produce un fallo.

1. Entorno reproducible#

Un proyecto reproducible declara sus dependencias y las instala en un entorno aislado. Con Python 3.10 o superior y uv:

uv init records-api --bare
cd records-api
uv add "fastapi[standard-no-fastapi-cloud-cli]"

uv add registra la dependencia en pyproject.toml, crea el entorno cuando hace falta y mantiene un archivo de bloqueo. Una alternativa válida es crear un venv e instalar fastapi[standard] con pip; no mezcles gestores en el mismo ejercicio.

[project]
name = "records-api"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = ["fastapi[standard-no-fastapi-cloud-cli]"]

[tool.fastapi]
entrypoint = "app.main:app"

2. Aplicación mínima#

from fastapi import FastAPI

app = FastAPI(
    title="Internal records",
    version="0.1.0",
)


@app.get("/health")
def health() -> dict[str, str]:
    return {"status": "ok"}

La línea app = FastAPI(...) crea la aplicación. El decorador registra que health atiende GET /health. La función no se ejecuta al importar el módulo: FastAPI conserva metadatos para invocarla cuando llegue una petición compatible.

Ejecuta:

uv run fastapi dev

El modo dev activa recarga y muestra información útil para desarrollo. La documentación interactiva aparece normalmente en /docs, la alternativa ReDoc en /redoc y el documento OpenAPI en /openapi.json.

3. Conserva el primer contrato como test#

La comprobación manual demuestra que el proceso y la red local funcionan. Un test pequeño conserva la misma promesa para el siguiente cambio:

uv add --dev pytest httpx
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)


def test_health_contract() -> None:
    response = client.get("/health")

    assert response.status_code == 200
    assert response.json() == {"status": "ok"}
uv run pytest

TestClient llama a la aplicación ASGI dentro del proceso de prueba: no abre un socket real. Por eso complementa a curl en vez de sustituirlo. La prueba conserva el contrato de la operación; curl observa además el servidor y la conexión local.

4. Uvicorn y ASGI#

La forma app.main:app se lee de izquierda a derecha:

  1. importa el módulo Python app.main;
  2. busca en él el atributo app;
  3. espera que ese objeto sea una aplicación ASGI.

La forma equivalente con Uvicorn es:

uv run uvicorn app.main:app --reload

Si estás dentro del directorio equivocado, el paquete no existe, falta un __init__.py necesario para tu estructura o el atributo tiene otro nombre, el servidor no puede importar la aplicación. Eso es un error de arranque, no un 404.

SeñalFrontera probablePrimera evidencia
ModuleNotFoundErrorImportaciónDirectorio actual, ruta de módulo y traceback.
“attribute app not found”Objeto ASGINombre exportado por el módulo.
404 Not FoundEnrutadoMétodo y ruta registrados en /openapi.json.
405 Method Not AllowedMétodo HTTPMisma ruta, método diferente.
422Contrato de entradaCuerpo de error y ubicación loc.

5. Flujo de una petición#

Recorrido de una interacción HTTP

El cliente envía una petición al proceso servidor HTTP, que la entrega a la aplicación. La aplicación procesa el recurso y devuelve el resultado al proceso servidor, que construye la respuesta para el cliente.

Para GET /health, el recorrido conceptual es:

  1. el cliente abre o reutiliza una conexión y envía el mensaje HTTP;
  2. Uvicorn traduce la interacción al protocolo ASGI;
  3. Starlette/FastAPI compara método y ruta;
  4. FastAPI extrae y valida entradas declaradas;
  5. llama a la operación de ruta;
  6. serializa y valida la salida cuando existe un contrato de respuesta;
  7. Uvicorn envía estado, headers y body al cliente.

No todas las etapas aparecen en el código de la función. Esa es precisamente la aportación del framework: ejecutar trabajo sistemático a partir de declaraciones.

6. Rutas, parámetros y orden#

@app.get("/records/me")
def current_record():
    return {"id": "me"}


@app.get("/records/{record_id}")
def read_record(record_id: str):
    return {"id": record_id}

Las rutas se evalúan en orden. Si declaras primero la ruta dinámica, /records/me puede tratar "me" como valor de record_id. La ruta estática más específica debe registrarse antes.

El nombre de la función no forma parte de la URL. Sí importan el método, el patrón del decorador y los tipos declarados.

7. Primer criterio entre def y async def#

FastAPI admite ambas formas:

@app.get("/sync")
def sync_operation():
    return {"mode": "sync"}


@app.get("/async")
async def async_operation():
    return {"mode": "async"}

Usa async def cuando llames directamente a una biblioteca awaitable y realmente vayas a escribir await. Usa def cuando la biblioteca relevante sea síncrona. No elijas por prestigio ni conviertas llamadas bloqueantes en no bloqueantes escribiendo async.

8. Diagnóstico mínimo basado en evidencia#

Antes de editar:

pwd
uv run python -c "from app.main import app; print(type(app).__name__)"
uv run fastapi dev

Después observa terminal, curl -i http://127.0.0.1:8000/health y /openapi.json. Una importación directa separa el problema de Python del problema de red. curl -i conserva estado y headers que el navegador puede ocultar.

9. Criterios de dominio#

Puedes considerar dominada esta unidad cuando:

  • creas un proyecto desde cero sin depender de un entorno global;
  • conservas el contrato de /health con una prueba que verifica status y body;
  • explicas cada parte de app.main:app;
  • diferencias error de importación, 404, 405 y 422;
  • predices la coincidencia de rutas estáticas y dinámicas;
  • explicas el recorrido cliente → Uvicorn → FastAPI → operación → respuesta;
  • justificas def o async def por la interfaz usada.

Prácticas relacionadas#

Fuentes y siguiente paso#

  • La Unidad 2.2 añade datos reales a ese recorrido: path, query, header, cookie, body, formulario y archivo.