Skip to main content

Run FastAPI dependency injection anywhere - in background tasks, scripts, and tests

Project description

fastapi-depends-anywhere

PyPI version Python versions CI Coverage License: MIT

Run FastAPI dependency injection anywhere - in background tasks, scripts, tests, and migrations.

Installation

# Using pip
pip install fastapi-depends-anywhere

# Using uv
uv add fastapi-depends-anywhere

# With asyncer support (for sync execution)
pip install fastapi-depends-anywhere[asyncer]

Quick Start

from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi_depends_anywhere import configure, with_fastapi_depends

app = FastAPI()

# Configure once at startup
configure(app=app)

async def get_db() -> Database:
    """Your database dependency."""
    return Database()

DbDep = Annotated[Database, Depends(get_db)]

@with_fastapi_depends
async def background_task(*, db: DbDep) -> None:
    """Background task that uses FastAPI dependencies."""
    await db.execute("INSERT INTO logs ...")

# Run it - dependencies are automatically resolved!
await background_task()

Why This Library?

FastAPI's dependency injection is powerful, but it only works inside route handlers. This library lets you use the same dependencies in:

  • Background tasks - Process jobs with full dependency access
  • Admin scripts - Run one-off scripts with database connections
  • Tests - Test functions that use dependencies without mocking everything
  • Migrations - Run database migrations with proper dependency injection
  • CLI commands - Build CLI tools that reuse your FastAPI dependencies

Core Features

with_fastapi_depends

Decorator that resolves FastAPI dependencies for any async or sync function:

from fastapi_depends_anywhere import with_fastapi_depends

@with_fastapi_depends
async def send_notification(user_id: int, *, db: DbDep, mailer: MailerDep) -> None:
    user = await db.get_user(user_id)
    await mailer.send(user.email, "Hello!")

# Dependencies are resolved automatically
await send_notification(123)

aiter_with_fastapi_depends

Decorator for async generators:

from fastapi_depends_anywhere import aiter_with_fastapi_depends

@aiter_with_fastapi_depends
async def stream_users(*, db: DbDep) -> AsyncGenerator[User, None]:
    async for user in db.stream_all_users():
        yield user

async for user in stream_users():
    print(user.name)

with_fastapi_lifecycle

Runs your function within FastAPI's lifespan context (startup/shutdown) when you're outside the normal request flow - such as in standalone scripts, CLI tools, or background workers:

from fastapi_depends_anywhere import with_fastapi_lifecycle

@with_fastapi_lifecycle
async def run_migration() -> None:
    # FastAPI's startup has run - resources are initialized!
    # (database connections, caches, etc.)
    async with get_db() as db:
        await run_all_migrations(db)
    # FastAPI's shutdown will run after this - cleanup happens automatically

# In a standalone script
asyncio.run(run_migration())

This is essential when your dependencies rely on resources initialized during FastAPI's lifespan (e.g., database connection pools, Redis clients, ML models).

runnify_with_fastapi_depends

For CLI scripts - combines lifecycle, dependency resolution, and sync execution:

from fastapi_depends_anywhere import runnify_with_fastapi_depends

@runnify_with_fastapi_depends
async def cli_report(*, db: DbDep) -> None:
    results = await db.fetch_all("SELECT * FROM reports")
    for row in results:
        print(row)

# No asyncio.run() needed!
if __name__ == "__main__":
    cli_report()

Requires the asyncer extra: pip install fastapi-depends-anywhere[asyncer]

Configuration

Global Configuration

Configure once at application startup:

from fastapi import FastAPI
from fastapi_depends_anywhere import configure

app = FastAPI()

# Basic configuration
configure(app=app)

# With context logging integration
from context_logging import Context
configure(app=app, context_factory=lambda ctx: Context(**ctx))

Explicit App Parameter

Or pass the app explicitly to each decorator:

@with_fastapi_depends(app=my_app)
async def my_function(*, db: DbDep) -> None:
    ...

Advanced Usage

Dependency Overrides

Dependency overrides work just like in FastAPI:

app.dependency_overrides[get_db] = get_test_db

@with_fastapi_depends
async def my_function(*, db: DbDep) -> None:
    # Uses get_test_db instead of get_db
    ...

Custom Request Scope

Pass custom ASGI scope data to dependencies that need it.

At decoration time (static scope):

@with_fastapi_depends(scope={"method": "POST", "path": "/custom"})
async def my_function(*, request: Request) -> None:
    print(request.method)  # "POST"

At call time (dynamic scope) - useful for background tasks that need request context:

@with_fastapi_depends
async def process_for_user(*, user: CurrentUserDep) -> None:
    # user is resolved from the passed scope's headers
    print(f"Processing for {user.id}")

@app.post("/trigger")
async def trigger(request: Request, background_tasks: BackgroundTasks) -> dict:
    # Pass the request scope to preserve auth headers, etc.
    background_tasks.add_task(process_for_user, _scope=request.scope)
    return {"status": "queued"}

This allows dependencies that read from Request (like auth) to work in background tasks:

from fastapi import Header

async def get_current_user(authorization: str = Header()) -> AuthUser:
    if authorization.startswith("Bearer "):
        return decode_token(authorization[7:])
    raise HTTPException(401)

CurrentUserDep = Annotated[AuthUser, Depends(get_current_user)]

@with_fastapi_depends
async def send_notification(*, user: CurrentUserDep, mailer: MailerDep) -> None:
    await mailer.send(user.email, "Task completed!")

# In your route - scope carries the auth headers
background_tasks.add_task(send_notification, _scope=request.scope)

Context Integration

For libraries like context_logging:

from context_logging import Context

configure(
    app=app,
    context_factory=lambda ctx: Context(**ctx)
)

@with_fastapi_depends(context={"request_id": "abc123"})
async def my_function(*, db: DbDep) -> None:
    # Context variables are set
    ...

Validation Utilities

Check if all routes and dependencies are async (recommended for context variable propagation):

from fastapi_depends_anywhere import check_routes_and_dependencies_are_async

@app.on_event("startup")
async def startup() -> None:
    check_routes_and_dependencies_are_async(app)

API Reference

Configuration

  • configure(app=None, context_factory=None) - Set global configuration
  • get_app() - Get the configured FastAPI app
  • reset_config() - Reset configuration (useful for tests)

Decorators

  • with_fastapi_depends(func, scope=None, context=None, app=None) - Resolve dependencies for a function
  • aiter_with_fastapi_depends(func, app=None) - Resolve dependencies for an async generator
  • with_fastapi_lifecycle(func, app=None) - Run within FastAPI lifespan (for scripts/workers outside request flow)
  • runnify_with_fastapi_depends(func, app=None) - Run async function synchronously with dependencies

Context Manager

  • resolve_fastapi_depends(func, scope=None, dependency_overrides_provider=None) - Low-level context manager for dependency resolution

Use Cases

Background Task with Dependencies

from fastapi import BackgroundTasks

@app.post("/users")
async def create_user(
    user: UserCreate,
    background_tasks: BackgroundTasks,
    db: DbDep,
) -> User:
    user = await db.create_user(user)
    background_tasks.add_task(send_welcome_email, user.id)
    return user

@with_fastapi_depends
async def send_welcome_email(user_id: int, *, db: DbDep, mailer: MailerDep) -> None:
    user = await db.get_user(user_id)
    await mailer.send(user.email, "Welcome!")

Admin Script

# scripts/cleanup_old_data.py
from myapp.main import app
from fastapi_depends_anywhere import configure, runnify_with_fastapi_depends

configure(app=app)

@runnify_with_fastapi_depends
async def cleanup(days: int = 30, *, db: DbDep) -> None:
    deleted = await db.delete_old_records(days)
    print(f"Deleted {deleted} records")

if __name__ == "__main__":
    import sys
    days = int(sys.argv[1]) if len(sys.argv) > 1 else 30
    cleanup(days)

Testing

import pytest
from fastapi_depends_anywhere import configure, with_fastapi_depends

@pytest.fixture
def app():
    app = FastAPI()
    configure(app=app)
    return app

async def test_my_function(app):
    @with_fastapi_depends
    async def my_function(*, db: DbDep) -> int:
        return await db.count_users()

    result = await my_function()
    assert result >= 0

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

License

MIT License - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_depends_anywhere-1.1.2.tar.gz (55.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_depends_anywhere-1.1.2-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_depends_anywhere-1.1.2.tar.gz.

File metadata

  • Download URL: fastapi_depends_anywhere-1.1.2.tar.gz
  • Upload date:
  • Size: 55.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fastapi_depends_anywhere-1.1.2.tar.gz
Algorithm Hash digest
SHA256 0015690ba99f63c73478e06ac87c24a727d0d218086cc8bab9ec8f38c9eb64fa
MD5 a2f616e5155d782ec7cc743c0988f218
BLAKE2b-256 20e4013e623c8165a74ba324394f82be20562ba665972ce8aed5928ff7f67ba0

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_depends_anywhere-1.1.2.tar.gz:

Publisher: release.yaml on ADR-007/fastapi-depends-anywhere

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_depends_anywhere-1.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_depends_anywhere-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ae7d97cc6b28189c137c3d8e225e7d36ffc285b28aa155fc4cd49d08853efa22
MD5 6787a4152bb48743be717ad57779982b
BLAKE2b-256 d1492ffde5ede02ded40c521e211c5d5e24ae6f363cb9eecbdd4778a1b74a240

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_depends_anywhere-1.1.2-py3-none-any.whl:

Publisher: release.yaml on ADR-007/fastapi-depends-anywhere

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page