Skip to content

Passive Dependencies

Passive dependencies (also known as path operation dependencies) are dependencies that run for every request but don't inject their return value into the endpoint function. They're useful for side effects like logging, authentication checks, or setting up request-scoped state.

FastIoC fully supports passive dependencies in endpoints, routers, and applications - just like regular FastAPI dependencies.

Endpoint Passive Dependencies

You can add passive dependencies to individual endpoints using the dependencies parameter:

# main.py
from fastapi import FastAPI, Depends
from project.interfaces import IAuthService, ILogService
from project.container import container

app = FastAPI()
container.injectify(app)

@app.get(
    '/protected',
    dependencies=[IAuthService, ILogService]  # pyright: ignore[reportArgumentType]
)
def protected_endpoint(data: str):
    # IAuthService and ILogService run before this function
    # but their return values are not injected here
    return {"data": data}

Important: Add # pyright: ignore[reportArgumentType] at the end of the line (or use your IDE's quick fix to ignore the error). Type checkers expect dependencies to be a list of Depends() objects, but FastIoC automatically converts your registered types to Depends() under the hood.

You can also mix FastIoC dependencies with traditional FastAPI dependencies:

from fastapi import Depends

def some_fastapi_dep():
    print("Running FastAPI dependency")

@app.get(
    '/mixed',
    dependencies=[IAuthService, Depends(some_fastapi_dep)]  # pyright: ignore[reportArgumentType]
)
def mixed_endpoint():
    return {"status": "ok"}

Router Passive Dependencies

Apply passive dependencies to all endpoints in a router:

# project/routers/admin.py
from fastapi import APIRouter, Depends
from project.interfaces import IAdminAuthService, IAuditService
from project.container import container

router = APIRouter(
    prefix="/admin",
    dependencies=[IAdminAuthService, IAuditService]  # pyright: ignore[reportArgumentType]
)

container.injectify(router)

@router.get('/users')
def list_admin_users():
    # IAdminAuthService and IAuditService run for every endpoint in this router
    return {"users": []}

@router.get('/settings')
def get_admin_settings():
    # Same dependencies run here too
    return {"settings": {}}

All endpoints in the router will automatically have these dependencies executed before the endpoint handler.

Application Passive Dependencies

Apply passive dependencies to all endpoints in your entire application (also known as Global Dependencies in FastAPI):

# main.py
from fastapi import FastAPI, Depends
from project.interfaces import IGlobalLogger, IRequestTracker
from project.container import container

app = FastAPI(
    dependencies=[IGlobalLogger, IRequestTracker]  # pyright: ignore[reportArgumentType]
)

container.injectify(app)

@app.get('/endpoint1')
def endpoint1():
    # IGlobalLogger and IRequestTracker run here
    return {"endpoint": 1}

@app.get('/endpoint2')
def endpoint2():
    # And here too - for every endpoint
    return {"endpoint": 2}

Every endpoint in the application will execute these dependencies.

Combining Passive Dependencies

You can combine passive dependencies at different levels - they all stack together:

# main.py
from fastapi import FastAPI, APIRouter, Depends
from project.interfaces import IGlobalLogger, IAuthService, IRateLimiter
from project.container import container

# Application-level: runs for ALL endpoints
app = FastAPI(
    dependencies=[IGlobalLogger]  # pyright: ignore[reportArgumentType]
)

# Router-level: runs for all endpoints in this router
admin_router = APIRouter(
    prefix="/admin",
    dependencies=[IAuthService]  # pyright: ignore[reportArgumentType]
)

container.injectify(app, admin_router)

# Endpoint-level: runs only for this specific endpoint
@admin_router.get(
    '/critical',
    dependencies=[IRateLimiter]  # pyright: ignore[reportArgumentType]
)
def critical_operation():
    # All three run: IGlobalLogger � IAuthService � IRateLimiter
    return {"status": "executed"}

@admin_router.get('/normal')
def normal_operation():
    # Only two run: IGlobalLogger � IAuthService
    return {"status": "ok"}

app.include_router(admin_router)

The dependencies execute in order from broadest to most specific: 1. Application-level dependencies 2. Router-level dependencies 3. Endpoint-level dependencies

Type Checker Warnings

When using FastIoC dependencies in the dependencies parameter, your IDE or type checker (like Pyright, mypy) will show an error because it expects Depends() objects:

# Type checker will complain about this:
@app.get('/endpoint', dependencies=[IUserService])
#                                    ^^^^^^^^^^^
# Expected type 'Depends', got 'type[IUserService]'

Solutions:

@app.get('/endpoint', dependencies=[IUserService])  # pyright: ignore[reportArgumentType]

Option 2: Type Ignore Comment

@app.get('/endpoint', dependencies=[IUserService])  # type: ignore

Option 3: IDE Quick Fix

Most IDEs (VS Code, PyCharm) offer quick fixes to suppress these warnings. Use the IDE's suggestion feature to add the appropriate ignore comment.

Why this happens: FastIoC intercepts these dependencies and converts them to Depends() automatically during the injectify process. The type checker doesn't know about this runtime transformation, so it sees a type mismatch.

Use Cases for Passive Dependencies

Passive dependencies are ideal for:

  • Authentication/Authorization - Validate tokens, check permissions
  • Logging/Monitoring - Log requests, track metrics
  • Rate Limiting - Throttle requests per user/IP
  • Request Validation - Check headers, validate API keys
  • Database Transactions - Set up transaction context
  • Caching - Check cache validity
  • Feature Flags - Enable/disable features per request
  • Audit Trails - Record who accessed what

Since passive dependencies don't inject their return values, they're perfect for side effects that don't need to pass data to the endpoint.

Next Steps