Dependency Injection

Intermedio
FastAPI
FastAPI
Actualizado: 01/07/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Concepto y sintaxis básica con Depends()

La inyección de dependencias es un patrón de diseño que permite que nuestras funciones reciban automáticamente los recursos o servicios que necesitan, sin tener que crearlos directamente dentro de cada función. En FastAPI, este mecanismo se implementa mediante la función Depends(), que actúa como un intermediario inteligente entre nuestras rutas y los recursos que requieren.

Imagina que tienes varias rutas en tu API que necesitan realizar la misma validación o preparar los mismos datos. Sin inyección de dependencias, tendrías que duplicar código en cada función. Con Depends(), defines esa lógica una sola vez y FastAPI se encarga de ejecutarla automáticamente cuando sea necesaria.

Sintaxis fundamental

La sintaxis básica de Depends() es sorprendentemente simple. Primero defines una función de dependencia que contiene la lógica que quieres reutilizar, y luego la inyectas en tus rutas usando Depends():

from fastapi import FastAPI, Depends

app = FastAPI()

def obtener_configuracion():
    return {"version": "1.0", "debug": True}

@app.get("/info")
def mostrar_info(config: dict = Depends(obtener_configuracion)):
    return {"mensaje": "API funcionando", "config": config}

En este ejemplo, obtener_configuracion() es nuestra función de dependencia. FastAPI la ejecuta automáticamente antes de llamar a mostrar_info(), y el resultado se pasa como parámetro config.

Cómo funciona internamente

Cuando FastAPI encuentra Depends() en los parámetros de una ruta, sigue este proceso automático:

  • 1. Identifica la dependencia: Reconoce que obtener_configuracion debe ejecutarse primero
  • 2. Ejecuta la función: Llama a obtener_configuracion() y obtiene su resultado
  • 3. Inyecta el resultado: Pasa el valor devuelto como argumento a la función de la ruta
def validar_usuario_activo():
    # Simulamos una validación
    usuario_activo = True
    if not usuario_activo:
        raise HTTPException(status_code=403, detail="Usuario inactivo")
    return {"usuario_id": 123, "activo": True}

@app.get("/perfil")
def obtener_perfil(usuario: dict = Depends(validar_usuario_activo)):
    return {"perfil": f"Datos del usuario {usuario['usuario_id']}"}

@app.get("/configuracion")
def obtener_configuracion_usuario(usuario: dict = Depends(validar_usuario_activo)):
    return {"configuracion": "Configuración personalizada", "usuario": usuario}

Observa cómo la misma función de dependencia se reutiliza en múltiples rutas. FastAPI ejecutará validar_usuario_activo() automáticamente para cada ruta que la declare como dependencia.

Dependencias con parámetros

Las funciones de dependencia también pueden recibir parámetros de la petición HTTP, como query parameters o path parameters:

def obtener_limite_paginacion(limite: int = 10, pagina: int = 1):
    if limite > 100:
        limite = 100
    offset = (pagina - 1) * limite
    return {"limite": limite, "offset": offset}

@app.get("/productos")
def listar_productos(paginacion: dict = Depends(obtener_limite_paginacion)):
    # Simulamos obtener productos con paginación
    productos = [f"Producto {i}" for i in range(paginacion["offset"], 
                                               paginacion["offset"] + paginacion["limite"])]
    return {"productos": productos, "paginacion": paginacion}

En este caso, FastAPI extrae automáticamente los parámetros limite y pagina de la URL (como ?limite=20&pagina=2) y los pasa a la función de dependencia.

Múltiples dependencias

Una ruta puede tener varias dependencias simultáneamente, y FastAPI las resolverá todas antes de ejecutar la función principal:

def obtener_timestamp():
    from datetime import datetime
    return datetime.now().isoformat()

def obtener_version_api():
    return "v2.1.0"

@app.get("/estado")
def estado_sistema(
    timestamp: str = Depends(obtener_timestamp),
    version: str = Depends(obtener_version_api),
    config: dict = Depends(obtener_configuracion)
):
    return {
        "estado": "operativo",
        "timestamp": timestamp,
        "version": version,
        "configuracion": config
    }

FastAPI ejecutará las tres funciones de dependencia en paralelo cuando sea posible, optimizando el rendimiento de tu API.

Ventajas inmediatas

El uso de Depends() aporta beneficios tangibles desde el primer momento:

  • Reutilización: Escribes la lógica una vez y la usas en múltiples rutas
  • Mantenibilidad: Los cambios en una dependencia se aplican automáticamente a todas las rutas que la usan
  • Testabilidad: Puedes probar las dependencias por separado y mockearlas fácilmente
  • Legibilidad: El código de las rutas se centra en su lógica específica, no en preparar datos

La inyección de dependencias con Depends() transforma tu código de FastAPI en algo más modular y profesional, eliminando la duplicación y facilitando el mantenimiento a largo plazo.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Dependencias simples reutilizables

Una vez que dominas la sintaxis básica de Depends(), el siguiente paso es crear dependencias reutilizables que resuelvan problemas comunes en tu API. Estas dependencias actúan como bloques de construcción que puedes combinar para crear funcionalidades más complejas sin duplicar código.

Dependencias para validación de datos

Las validaciones personalizadas son uno de los casos de uso más frecuentes para las dependencias reutilizables. En lugar de repetir la misma lógica de validación en múltiples rutas, puedes encapsularla en una dependencia:

from fastapi import FastAPI, Depends, HTTPException

app = FastAPI()

def validar_id_positivo(id: int):
    if id <= 0:
        raise HTTPException(status_code=400, detail="El ID debe ser un número positivo")
    return id

@app.get("/usuarios/{user_id}")
def obtener_usuario(user_id: int = Depends(validar_id_positivo)):
    return {"usuario_id": user_id, "nombre": f"Usuario {user_id}"}

@app.get("/productos/{product_id}")
def obtener_producto(product_id: int = Depends(validar_id_positivo)):
    return {"producto_id": product_id, "nombre": f"Producto {product_id}"}

Esta dependencia garantiza consistencia en la validación de IDs a lo largo de toda tu API. Si necesitas cambiar la lógica de validación, solo tienes que modificarla en un lugar.

Dependencias para formateo de respuestas

Otra aplicación práctica es crear dependencias que preparen datos de forma consistente para tus respuestas:

def obtener_metadatos_respuesta():
    from datetime import datetime
    return {
        "timestamp": datetime.now().isoformat(),
        "servidor": "api-v1",
        "zona_horaria": "UTC"
    }

def formatear_respuesta_exitosa(datos: dict, metadatos: dict = Depends(obtener_metadatos_respuesta)):
    return {
        "exito": True,
        "datos": datos,
        "metadatos": metadatos
    }

@app.get("/estadisticas")
def obtener_estadisticas(respuesta_base: dict = Depends(formatear_respuesta_exitosa)):
    estadisticas = {"usuarios_activos": 150, "productos_vendidos": 1200}
    respuesta_base["datos"] = estadisticas
    return respuesta_base

Dependencias con lógica de negocio

Las dependencias también pueden encapsular reglas de negocio específicas de tu aplicación:

def calcular_descuento_usuario(tipo_usuario: str = "regular"):
    descuentos = {
        "premium": 0.15,
        "vip": 0.25,
        "regular": 0.05
    }
    descuento = descuentos.get(tipo_usuario, 0.05)
    return {"tipo": tipo_usuario, "descuento": descuento}

@app.get("/precio/{producto_id}")
def calcular_precio_final(
    producto_id: int,
    precio_base: float = 100.0,
    descuento_info: dict = Depends(calcular_descuento_usuario)
):
    precio_final = precio_base * (1 - descuento_info["descuento"])
    return {
        "producto_id": producto_id,
        "precio_base": precio_base,
        "descuento_aplicado": descuento_info["descuento"],
        "precio_final": round(precio_final, 2)
    }

Combinando múltiples dependencias reutilizables

La verdadera potencia surge cuando combinas varias dependencias reutilizables en una sola ruta:

def obtener_configuracion_cache():
    return {"cache_activo": True, "tiempo_expiracion": 300}

def generar_id_peticion():
    import uuid
    return str(uuid.uuid4())[:8]

@app.get("/reporte/{reporte_id}")
def generar_reporte(
    reporte_id: int = Depends(validar_id_positivo),
    id_peticion: str = Depends(generar_id_peticion),
    config_cache: dict = Depends(obtener_configuracion_cache),
    metadatos: dict = Depends(obtener_metadatos_respuesta)
):
    return {
        "reporte_id": reporte_id,
        "id_peticion": id_peticion,
        "datos_reporte": f"Contenido del reporte {reporte_id}",
        "cache": config_cache,
        "metadatos": metadatos
    }

Dependencias con parámetros configurables

Para mayor flexibilidad, puedes crear dependencias parametrizables que se adapten a diferentes situaciones:

def crear_validador_rango(minimo: int, maximo: int):
    def validar_rango(valor: int):
        if valor < minimo or valor > maximo:
            raise HTTPException(
                status_code=400, 
                detail=f"El valor debe estar entre {minimo} y {maximo}"
            )
        return valor
    return validar_rango

# Crear validadores específicos
validar_edad = crear_validador_rango(0, 120)
validar_puntuacion = crear_validador_rango(1, 10)

@app.post("/usuario")
def crear_usuario(edad: int = Depends(validar_edad)):
    return {"mensaje": "Usuario creado", "edad": edad}

@app.post("/resena")
def crear_resena(puntuacion: int = Depends(validar_puntuacion)):
    return {"mensaje": "Reseña creada", "puntuacion": puntuacion}

Organizando dependencias en módulos

A medida que tu API crece, es recomendable organizar las dependencias en archivos separados para mantener el código limpio:

# archivo: dependencias/validaciones.py
from fastapi import HTTPException

def validar_email_formato(email: str):
    import re
    patron = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    if not re.match(patron, email):
        raise HTTPException(status_code=400, detail="Formato de email inválido")
    return email.lower()

# archivo: main.py
from fastapi import FastAPI, Depends
from dependencias.validaciones import validar_email_formato

app = FastAPI()

@app.post("/suscripcion")
def crear_suscripcion(email: str = Depends(validar_email_formato)):
    return {"mensaje": "Suscripción creada", "email": email}

Las dependencias reutilizables transforman tu código en un conjunto de componentes modulares que puedes combinar de diferentes formas. Esto no solo reduce la duplicación, sino que hace que tu API sea más fácil de mantener, probar y extender a medida que crece.

Aprendizajes de esta lección

  • Comprender el concepto y la sintaxis básica de la inyección de dependencias con Depends() en FastAPI
  • Aprender a reutilizar funciones de dependencia para validación, formateo y lógica de negocio
  • Saber cómo combinar múltiples dependencias en una sola ruta para optimizar el código
  • Entender cómo crear dependencias parametrizables y organizarlas en módulos para mantener el código limpio
  • Reconocer las ventajas de la inyección de dependencias en términos de reutilización, mantenibilidad y testabilidad

Completa FastAPI y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración