Elegir espacio

Dos espacios

¿Qué quieres consultar?

Elige el espacio al que quieres entrar.

Recorrido · FastAPIBloque II · Unidad 2.1

Bloque II · Módulo 2 · Unidad 2.1

Entorno, aplicación mínima, Uvicorn y flujo de una petición

Crear un proyecto reproducible, ejecutar FastAPI en desarrollo y seguir una petición desde Uvicorn hasta una operación de ruta.

Objetivo y frontera#

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 expediente-api --bare
cd expediente-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 = "expediente-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="Expedientes internos",
    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. 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.

4. 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.

5. Rutas, parámetros y orden#

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


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

Las rutas se evalúan en orden. Si declaras primero la ruta dinámica, /expedientes/me puede tratar "me" como valor de expediente_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.

6. 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.

7. 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.

8. Criterios de dominio#

Puedes considerar dominada esta unidad cuando:

  • creas un proyecto desde cero sin depender de un entorno global;
  • 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.