FastAPI Inyección de Dependencias

FastAPI proporciona un sistema de inyección de dependencias potente y sencillo.

La inyección de dependencias es un patrón de diseño que te permite extraer lógica común (como conexiones de base de datos, autenticación, validación de parámetros, etc.) en componentes reutilizables y luego usarlos según sea necesario en las rutas.


¿Qué es la inyección de dependencias?

En pocas palabras,una dependenciaes una función que puede usar los mismos parámetros que las funciones de operación de ruta (parámetros de consulta, parámetros de ruta, cuerpo de la solicitud, etc.). FastAPI llama automáticamente a la función de dependencia antes de ejecutar la función de ruta y pasa su valor de retorno a la función de ruta.

Ejemplo

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# 1. Definir la función de dependencia
def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


# 2. Usar la dependencia en la ruta
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    # commons recibe el valor de retorno de la función de dependencia
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    # Varias rutas pueden reutilizar la misma dependencia
    return commons

Explicación del código:

ParteDescripción
common_parametersFunción de dependencia, maneja parámetros de consulta comunes
Depends(common_parameters)Declarar parámetrocommonsel valor proviene de la función de dependencia
commons: dictRecibe el diccionario devuelto por la función de dependencia

Las funciones de dependencia y las funciones de operación de ruta usan la misma forma de declarar parámetros. FastAPI resuelve automáticamente los parámetros de la función de dependencia. Esto significa que la función de dependencia también puede usar parámetros de consulta, parámetros de ruta, cuerpo de la solicitud, etc.


Flujo de ejecución de dependencias

Cuando llega una solicitud, el orden de procesamiento de FastAPI:

  1. Identificar la función de ruta y sus dependencias
  2. Ejecutar las funciones de dependencia (en orden de dependencia)
  3. Pasar los valores de retorno de las funciones de dependencia a la función de ruta
  4. Ejecutar la función de ruta

Clases como dependencias

Además de funciones, también puedes usar clases como dependencias:

Ejemplo

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Declarar dependencia con una clase
class CommonQueryParams:
    def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


# Usar la clase como dependencia
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
    return {"q": commons.q, "skip": commons.skip, "limit": commons.limit}

CuandoDepends()Cuando no se pasa un parámetro, FastAPI usa automáticamente la anotación de tipo del parámetro (CommonQueryParams) como dependencia.


Sub-dependencias

Las dependencias pueden tener sus propias dependencias, formando una cadena de dependencias:

Ejemplo

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Función de dependencia
def query_extractor(q: str | None = None):
    return q


# Sub-dependencia: depende de query_extractor
def query_checker(q: str = Depends(query_extractor)):
    if q == "admin":
        # La sub-dependencia puede realizar validación
        return q + " (checked)"
    return q


# La ruta usa la sub-dependencia
@app.get("/items/")
async def read_items(q: str = Depends(query_checker)):
    return {"q": q}

Flujo de ejecución:query_extractor -> query_checker-> Función de ruta


Usar dependencias en decoradores

A veces solo necesitas los efectos secundarios de la dependencia (como verificación de permisos), no su valor de retorno. Puedes declararlo en el parámetrodependenciesdel decorador:

Ejemplo

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


# Dependencia: validar API Key
async def verify_api_key(x_api_key: str = Header()):
    if x_api_key != "secret-key":
        raise HTTPException(status_code=400, detail="X-API-Key invalid")


# Usar la dependencia en el decorador, sin necesidad de valor de retorno
@app.get("/items/", dependencies=[Depends(verify_api_key)])
async def read_items():
    return [{"item": "Foo"}]


# Usar la dependencia para todo el grupo de rutas
@app.get("/users/", dependencies=[Depends(verify_api_key)])
async def read_users():
    return [{"user": "Bar"}]

Dependencias globales

Se puedenFastAPIdeclarar dependencias globales en la instancia, que se aplican a todas las rutas:

Ejemplo

from fastapi import Depends, FastAPI, Header, HTTPException

async def verify_token(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


# Dependencia global: todas las rutas requieren validación de token
app = FastAPI(dependencies=[Depends(verify_token)])


@app.get("/items/")
async def read_items():
    return [{"item": "Foo"}]


@app.get("/users/")
async def read_users():
    return [{"user": "Bar"}]

Dependencias con yield

Cuando la dependencia necesita realizar operaciones de limpieza (como cerrar la conexión de la base de datos), usayield:

Ejemplo

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Dependencia con yield: crea la conexión antes de la solicitud, la cierra después de la solicitud
def get_db():
    db = "database_connection"  # Simular la creación de una conexión de base de datos
    try:
        yield db  # Proporcionar la conexión de base de datos durante el procesamiento de la solicitud
    finally:
        print("Cerrar la conexión de base de datos")  # Limpiar recursos después de completar la solicitud


@app.get("/items/")
async def read_items(db: str = Depends(get_db)):
    return {"db": db}

Flujo de ejecución:

  1. Cuando llega la solicitud, se ejecutayieldel código anterior, creando la conexión de base de datos
  2. yieldSe pasa la conexión a la función de ruta
  3. Después de que la función de ruta termina de ejecutarse, se ejecutafinallyel código de limpieza en el bloque

yieldLas dependencias son muy adecuadas para gestionar recursos que necesitan limpieza, como conexiones de base de datos, manejadores de archivos, etc. Independientemente de si la función de ruta lanza una excepción, el código de limpieza se ejecutará.


Resumen

  • La inyección de dependencias extrae lógica común en funciones o clases reutilizables
  • UsarDepends()para declarar dependencias en los parámetros de la función de ruta
  • Las dependencias pueden anidarse, formando cadenas de dependencias
  • El parámetrodependenciesdel decorador se usa para dependencias que no necesitan valor de retorno
  • Las dependencias que usanyieldadmiten la limpieza de recursos
  • Las dependencias globales se aplican a todas las rutas
Otras extensiones