Skip to content

FastAPI

APIs agiles con Python y FastAPI

FastAPI fue construido por el desarrollador colombiano Sebastián Ramirez, se trata de un marco de trabajo web moderno y de alto rendimiento para construir APIs con Python 3.7+ basado en type hints estándar. Se destaca por su velocidad, facilidad de uso y generación automática de documentación.

Lo que hace atractivo este framework para el desrrallo de una API bajo Python son caractarísticas tales como:

  • Validación automática: Usa type hints de Python para validar datos de entrada y salida
  • Documentación automática: Genera OpenAPI (Swagger) y ReDoc sin configuración adicional
  • Alto rendimiento: Basado en Starlette y Pydantic, comparable a NodeJS y Go
  • Soporte async/await: Programación asíncrona nativa para operaciones I/O
  • Inyección de dependencias: Sistema integrado para gestionar dependencias

Instalación de FastAPI

bash
pip install fastapi
pip install uvicorn[standard]

Uvicorn es el servidor ASGI recomendado para ejecutar FastAPI en producción.
Es conveniente usar entornos virtuales para instalación, por ejemplo, si usas sistema macOS.

Primera API con FastAPI

Crea un archivo main.py:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

Ejecuta el servidor:

bash
uvicorn main:app --reload

La API estará disponible en http://127.0.0.1:8000 La documentación automática se genera en http://127.0.0.1:8000/docs (Swagger UI) o /redoc (ReDoc)

Estructura de proyecto recomendada

myapi/
├── app/
│   ├── __init__.py
│   ├── main.py          # Punto de entrada de la aplicación
│   ├── models.py        # Modelos Pydantic (esquemas de datos)
│   ├── routers/         # Rutas organizadas por recurso
│   │   ├── __init__.py
│   │   ├── items.py
│   │   └── users.py
│   ├── dependencies.py  # Dependencias inyectables
│   └── database.py      # Configuración de base de datos
├── requirements.txt
└── .env

Conceptos clave

Path Operations (Operaciones de ruta)

python
@app.get("/items")      # Leer recursos
@app.post("/items")     # Crear recursos
@app.put("/items/{id}") # Actualizar completo
@app.patch("/items/{id}") # Actualizar parcial
@app.delete("/items/{id}") # Eliminar recursos

Modelos con Pydantic

python
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None

@app.post("/items/")
def create_item(item: Item):
    return item

Parámetros de consulta y cuerpo

python
from fastapi import Query, Path, Body

@app.get("/items/")
def read_items(
    q: str = Query(None, max_length=50),
    skip: int = 0,
    limit: int = 100
):
    return {"q": q, "skip": skip, "limit": limit}

@app.put("/items/{item_id}")
def update_item(
    item_id: int = Path(..., title="ID del item"),
    item: Item = Body(...)
):
    return {"item_id": item_id, **item.dict()}

Inyección de dependencias

python
from fastapi import Depends

def get_query_token(token: str):
    if token != "fake-super-secret-token":
        raise HTTPException(status_code=403, detail="Token inválido")
    return token

@app.get("/items/")
def read_items(token: str = Depends(get_query_token)):
    return [{"item": "Foo", "token": token}]

Respuestas y status codes

python
from fastapi import Response, status

@app.post("/items/", status_code=status.HTTP_201_CREATED)
def create_item(item: Item, response: Response):
    response.headers["X-Custom-Header"] = "custom-value"
    return item

Middleware y CORS

python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Base de datos con SQLAlchemy

python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# Dependencia para obtener sesión de DB
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

Testing

python
from fastapi.testclient import TestClient

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"Hello": "World"}

def test_read_item():
    response = client.get("/items/42?q=test")
    assert response.status_code == 200
    assert response.json() == {"item_id": 42, "q": "test"}

Despliegue

bash
# Desarrollo
uvicorn main:app --reload --host 0.0.0.0 --port 8000

# Producción
uvicorn main:app --host 0.0.0.0 --port 80 --workers 4

# Con Gunicorn (para múltiples workers)
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker

Recursos oficiales