Appearance
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 uvicornUvicorn 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.
Usando entorno virtual
Los entornos virtuales permiten conservar el conjunto de depedencias de un proyecto específico, evitando conflictos por uso de versiones distintas entre diversos proyectos. Además ofrecen la versiónn de Python que requiere tu proyecto. Es decir, proporcionan un entorno específico para tu proyecto (de modo virtual).
Es probable que con la instalación de Python encuentres venv. De esta manera, el comando pip install fastapi uvicorn (de arriba) se debe ejecutar después de establecer lo siguiente...
bash
python3 -m venv ./venv
source venv/bin/activate
./venvse refiere a la ruta en la que se gestiona el entorno virtual.deactivatees el comando usado para salir del entorno virtual.
Primer contacto con FastAPI
Crea un archivo main.py, agrega y observa el siguiente código:
python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hi": "there!"}Ejecuta el servidor:
bash
uvicorn main:app --reloadLa API estará disponible en
http://127.0.0.1:8000La documentación automática se genera enhttp://127.0.0.1:8000/docs(Swagger UI) o/redoc(ReDoc)
Siguiente API con FastAPI
A continuación tendremos otro ejemplo sencillo en el que involucramos un router para organizar la gestión del endpoint. De esa manera, creaermeos tres (3) archivos Python: main.py, router.py, endpoint.py. Veamos cada uno...
main.py
python
from fastapi import FastAPI
from router import router
app = FastAPI()
app.include_router(router)
@app.get("/")
def read_root():
return {"Hi": "there!"}router.py
python
from fastapi import APIRouter
import endpoint
router = APIRouter()
router.include_router(endpoint.router, prefix="/actions", tags=["actions"])endpoint.py
python
import json
from http import HTTPStatus
from fastapi import APIRouter
from starlette.responses import Response
router = APIRouter()
@router.post("/", dependencies=[])
def handle_event(data: dict) -> Response:
print(data)
return Response(
content=json.dumps({"message": "Data processed!"}),
status_code=HTTPStatus.ACCEPTED,
media_type="application/json",
)Nótese que se define el
endpointy se inporta enrouterbajo la ruta/actions, y enmainse incluye elrouter.
Recuerda lanzar el servicio con comando:uvicorn main:app --reload
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
└── .envConceptos 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 recursosModelos 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 itemPará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 itemMiddleware 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.UvicornWorkerChat con FastAPI & OpenIA API
Dada las facilidades de FastAPI para hacer una API en Python, y del lenguaje para interactuar con modelos de IA, resulta propicio para un ejercicio que he publicado en GitHub sobre Chat con FastAPI & OpenIA API