Run FastAPI dependency injection anywhere - in background tasks, scripts, and tests
Project description
fastapi-depends-anywhere
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 configurationget_app()- Get the configured FastAPI appreset_config()- Reset configuration (useful for tests)
Decorators
with_fastapi_depends(func, scope=None, context=None, app=None)- Resolve dependencies for a functionaiter_with_fastapi_depends(func, app=None)- Resolve dependencies for an async generatorwith_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_depends_anywhere-1.1.1.tar.gz.
File metadata
- Download URL: fastapi_depends_anywhere-1.1.1.tar.gz
- Upload date:
- Size: 55.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e77e9052025481a6343ef7c65ed9fd4a69f0f389d89ec9b903c5ac3946610ecd
|
|
| MD5 |
c30021984337fd782470197edc43cd3f
|
|
| BLAKE2b-256 |
fa3975451b4741612179c80219a21fda1609692aa0f5df127a1568dba09e3dff
|
Provenance
The following attestation bundles were made for fastapi_depends_anywhere-1.1.1.tar.gz:
Publisher:
release.yaml on ADR-007/fastapi-depends-anywhere
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_depends_anywhere-1.1.1.tar.gz -
Subject digest:
e77e9052025481a6343ef7c65ed9fd4a69f0f389d89ec9b903c5ac3946610ecd - Sigstore transparency entry: 767459926
- Sigstore integration time:
-
Permalink:
ADR-007/fastapi-depends-anywhere@5e2aaf1c49d3cca380aebc89dffb73c3b249d4d6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ADR-007
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@5e2aaf1c49d3cca380aebc89dffb73c3b249d4d6 -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file fastapi_depends_anywhere-1.1.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_depends_anywhere-1.1.1-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
981bb9ce38d64a6a49e07fd0b0cbd46ce7c484e9713d5ea7573dab26d10d6333
|
|
| MD5 |
3e45ecd8342949b18a00bd2d7c1f681c
|
|
| BLAKE2b-256 |
2afe41f4ec0c31c2fef67297d7451fe2ab69241eacf2922ae9686fc88a6d8922
|
Provenance
The following attestation bundles were made for fastapi_depends_anywhere-1.1.1-py3-none-any.whl:
Publisher:
release.yaml on ADR-007/fastapi-depends-anywhere
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_depends_anywhere-1.1.1-py3-none-any.whl -
Subject digest:
981bb9ce38d64a6a49e07fd0b0cbd46ce7c484e9713d5ea7573dab26d10d6333 - Sigstore transparency entry: 767459931
- Sigstore integration time:
-
Permalink:
ADR-007/fastapi-depends-anywhere@5e2aaf1c49d3cca380aebc89dffb73c3b249d4d6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ADR-007
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@5e2aaf1c49d3cca380aebc89dffb73c3b249d4d6 -
Trigger Event:
workflow_run
-
Statement type: