Las dependencias de FastAPI declaran lo que una ruta necesita sin repetir autenticación, paginación o sesiones. Revisa primero cómo crear una API con FastAPI.
from typing import Annotated
from fastapi import Depends, FastAPI, Query
app = FastAPI()
def paginacion(pagina: Annotated[int, Query(ge=1)] = 1, limite: int = 20):
return pagina, min(limite, 100)
Paginacion = Annotated[tuple[int, int], Depends(paginacion)]
La guía oficial explica el estilo con Annotated. Una dependencia puede depender de otra; FastAPI resuelve el grafo y reutiliza resultados dentro del request.
Recursos con yield
def obtener_sesion():
sesion = SessionLocal()
try:
yield sesion
finally:
sesion.close()
Define claramente commit y rollback. Consulta la guía de SQLAlchemy.
Construir una cadena de autenticación
Separa extracción, validación y autorización:
from fastapi import Header, HTTPException
def token_bearer(authorization: Annotated[str | None, Header()] = None) -> str:
if authorization is None or not authorization.startswith("Bearer "):
raise HTTPException(
status_code=401,
detail="Falta el token",
headers={"WWW-Authenticate": "Bearer"},
)
return authorization.removeprefix("Bearer ")
def usuario_actual(token: Annotated[str, Depends(token_bearer)]) -> Usuario:
usuario = servicio_token.validar(token)
if usuario is None:
raise HTTPException(status_code=401, detail="Token inválido")
return usuario
def admin_activo(usuario: Annotated[Usuario, Depends(usuario_actual)]) -> Usuario:
if not usuario.activo or "admin" not in usuario.roles:
raise HTTPException(status_code=403, detail="Permiso insuficiente")
return usuario
Una credencial inválida produce 401; un usuario autenticado sin permiso, 403. No recibas tokens por URL porque suele registrarse. La verificación real debe comprobar firma, algoritmo, expiración, emisor y audiencia cuando corresponda.
Configurar con clases invocables
class ExigirRol:
def __init__(self, rol: str):
self.rol = rol
def __call__(self, usuario: Annotated[Usuario, Depends(usuario_actual)]):
if self.rol not in usuario.roles:
raise HTTPException(status_code=403, detail="Permiso insuficiente")
return usuario
exigir_editor = ExigirRol("editor")
Cualquier objeto invocable puede ser dependencia. Crea configuraciones al iniciar y evita estado mutable de request en instancias compartidas.
Tratar yield como un contexto
def obtener_sesion():
with SessionLocal() as sesion:
try:
yield sesion
sesion.commit()
except Exception:
sesion.rollback()
raise
Hacer commit aquí es una decisión arquitectónica. Puede ser cómodo, pero oculta límites de transacción. Muchos equipos abren y cierran en la dependencia y hacen commit en el servicio. Documenta la convención y prueba rollback. La guía oficial de yield explica la limpieza.
Usa async def cuando esperas un driver asíncrono. Mover trabajo bloqueante a async def no lo vuelve no bloqueante.
Entender la caché por request
Una dependencia repetida en el grafo normalmente se ejecuta una vez y reutiliza el valor en ese request. Resulta útil para usuario y sesión, pero no es caché global.
def nonce_request() -> str:
return secrets.token_urlsafe(16)
Nonce = Annotated[str, Depends(nonce_request, use_cache=False)]
Usa use_cache=False solo si cada declaración requiere un valor nuevo. La caché entre requests necesita expiración e invalidación explícitas.
Aplicar una dependencia sin recibir valor
def exigir_clave(x_api_key: Annotated[str, Header()]) -> None:
if not secrets.compare_digest(x_api_key, settings.clave_interna):
raise HTTPException(status_code=401, detail="Clave inválida")
@app.get("/interno/salud", dependencies=[Depends(exigir_clave)])
def salud_interna():
return {"status": "ok"}
Las dependencias de router protegen un grupo. Las globales afectan todo, incluida documentación y health checks dentro del alcance. Efectos como auditoría deben ser explícitos y resistentes.
Sustituir dependencias en pruebas
El mapping usa el callable original como clave:
app.dependency_overrides[obtener_sesion] = sesion_falsa
try:
assert client.get("/productos").status_code == 200
finally:
app.dependency_overrides.clear()
Limpia overrides porque la aplicación suele sobrevivir entre pruebas. Una fixture puede instalar y retirar el mapping. Conserva integración para configuración real, transacciones y tokens.
Evita devolver un contenedor enorme para que las rutas busquen servicios arbitrarios. Oculta acoplamiento. Las dependencias coordinan credenciales, configuración, sesiones y construcción ligera; las decisiones de dominio pertenecen a servicios comprobables sin FastAPI. Revisa dependencias que repiten llamadas, mutan estado global o capturan excepciones amplias. Comprueba también el OpenAPI generado y nunca incluyas secretos como ejemplos.
Depurar el grafo
Si una ruta devuelve 422, identifica qué parámetro no pudo validar FastAPI. Un valor declarado como query en vez de header impide ejecutar la ruta. Usa anotaciones explícitas y revisa en OpenAPI su ubicación y restricciones.
Si el trabajo se repite, observa la identidad de los callables. Dos wrappers con la misma lógica son dependencias distintas y no comparten resultado. Si hay datos entre usuarios, recuerda que la caché interna dura un request; busca objetos globales, caché de aplicación o servicios mutables.
Mide duración en fronteras lentas sin registrar tokens ni datos personales. Una dependencia universal que llama un servicio remoto añade latencia a todas las rutas y amplía una caída. Divide el grafo según necesidades reales.
Definir responsabilidades
La configuración puede ser un objeto inmutable del inicio y la sesión un recurso del request. Aprobar pedidos, calcular descuentos o decidir elegibilidad son reglas de dominio comprobables sin el framework.
No captures toda Exception para devolver un mensaje genérico. Garantiza limpieza con finally, registra fallos operativos de modo seguro y traduce errores conocidos en la frontera HTTP. Convertir todo en 401 o 500 hace reaccionar mal a clientes.
Mantén dependencias pequeñas. Si una función valida token, busca usuario, abre transacción, consulta permisos y registra auditoría, cambios no relacionados afectan al mismo componente. Divide y compón.
Probar casos reveladores
Cubre entrada ausente, credencial malformada, token vencido, usuario inactivo, permiso insuficiente, cierre tras excepción y requests independientes. Verifica WWW-Authenticate cuando corresponda. Estos casos demuestran aislamiento.
En pruebas de ruta, sustituye servicios lentos. En integración, ejecuta wiring real, transacciones y validación de token. Si yield forma parte del contrato, el reemplazo también debe representar adquisición y limpieza.
Evita overrides globales instalados al importar tests. Usa fixture con try y finally, pues una aserción interrumpida no debe contaminar el siguiente escenario. Si hay varias aplicaciones, modifica la instancia exacta del cliente.
Elegir el alcance
Una regla común puede declararse en APIRouter, haciendo visible el límite de seguridad. Si se coloca solo en dependencies=[...], su valor se descarta; cuando la ruta necesita al usuario, recíbelo como parámetro tipado.
Las dependencias globales deben ser raras porque pueden bloquear documentación, health checks o endpoints públicos. Revisa las rutas antes de ampliar alcance y conserva monitorización compatible con seguridad.
Los grafos profundos también merecen revisión. Pueden ser correctos, pero si orden y efectos no son claros, extrae servicios explícitos. La inyección debe mostrar requisitos, no producir ejecuciones sorprendentes.
Revisar contrato y operación
Lee la documentación generada como consumidor. Headers, errores, modelos y descripciones deben coincidir con el comportamiento. Los parámetros de dependencias aparecen en OpenAPI, por lo que nombres y validaciones deben ser estables.
Define timeout y política de indisponibilidad para llamadas externas. Autenticación normalmente debe fallar cerrada; telemetría opcional puede fallar sin detener la operación. La elección debe ser consciente.
Durante revisión, busca estado compartido, clientes creados por request sin necesidad, sesiones abiertas y mensajes con detalles internos. Tipos de retorno y nombres claros facilitan mantenimiento.
La inyección no elimina acoplamiento; lo hace explícito y sustituible. El resultado buscado es una ruta legible, reglas de dominio independientes y recursos cerrados correctamente tanto en éxito como en fallo.
Antes de publicar, prueba también la documentación interactiva y una llamada fuera de ella. Confirma que el cliente sabe qué header enviar, qué respuesta recibe cuando falta y cómo distinguir autenticación de autorización. Los ejemplos no deben contener credenciales reales.
En producción, observa latencia y errores por dependencia para localizar el límite lento. Una métrica global de la ruta no revela si el tiempo se consume al validar token, abrir sesión o consultar permisos. Registra identificadores de correlación, pero nunca el token completo.
Si una dependencia necesita configuración distinta por entorno, cárgala desde una fuente persistente y validada al iniciar. Fallar al inicio por una clave obligatoria ausente es preferible a descubrir el problema en la primera petición. Mantén el objeto de configuración inmutable para evitar cambios accidentales entre usuarios.