Spring-like Dependency Injection and Autowiring for FastAPI
Project description
FastAPI Autowire
Spring-like Dependency Injection and Autowiring for FastAPI
Overview
FastAPI Autowire brings Spring-style dependency injection to FastAPI applications. It provides a clean, declarative way to manage dependencies with automatic resolution, lifecycle management, and type-safe autowiring.
Features
- 🔧 Automatic Dependency Resolution - Dependencies are automatically resolved and injected
- 🏷️ Semantic Decorators - Use
@service,@repository,@component,@configuration, and@provider - 🔄 Lifecycle Management - Built-in support for
post_constructandshutdownhooks - 🎯 Type-Safe Autowiring - Leverage Python's type hints with the
Autowired[T]generic - 🌳 Circular Dependency Detection - Prevents circular dependencies at startup
- 📦 Interface-Based Registration - Register implementations against abstract base classes
- ⚡ FastAPI Integration - Seamless integration with FastAPI's dependency injection system
Installation
pip install fastapi-autowire
Or using uv:
uv add fastapi-autowire
Quick Start
1. Define Your Components
from fastapi_autowire import service, repository, Autowired
@repository
class UserRepository:
async def get_user(self, user_id: int):
# Database logic here
return {"id": user_id, "name": "John Doe"}
@service
class UserService:
def __init__(self, user_repo: Autowired[UserRepository]):
self.user_repo = user_repo
async def get_user(self, user_id: int):
return await self.user_repo.get_user(user_id)
2. Setup Your FastAPI Application
from fastapi import FastAPI
from fastapi_autowire import lifespan, Autowired
app = FastAPI(lifespan=lifespan)
@app.get("/users/{user_id}")
async def get_user(user_id: int, service: Autowired[UserService]):
return await service.get_user(user_id)
That's it! The library handles:
- ✅ Component registration
- ✅ Dependency resolution
- ✅ Lifecycle management
- ✅ Injection into route handlers
Core Concepts
Component Registration
Use semantic decorators to register components:
from fastapi_autowire import service, repository, component, configuration, provider
@service
class EmailService:
pass
@repository
class UserRepository:
pass
@component
class CacheManager:
pass
@configuration
class AppConfig:
pass
@provider
class DatabaseProvider:
pass
All decorators are aliases of @component and serve as semantic markers for better code organization.
Dependency Injection
Constructor Injection
from fastapi_autowire import service, Autowired
@service
class OrderService:
def __init__(
self,
user_service: Autowired[UserService],
payment_service: Autowired[PaymentService]
):
self.user_service = user_service
self.payment_service = payment_service
Route Handler Injection
from fastapi import FastAPI
from fastapi_autowire import Autowired
@app.get("/orders")
async def get_orders(order_service: Autowired[OrderService]):
return await order_service.get_all_orders()
Interface-Based Registration
Register concrete implementations against abstract interfaces:
from abc import ABC, abstractmethod
from fastapi_autowire import service
class IEmailService(ABC):
@abstractmethod
async def send_email(self, to: str, subject: str, body: str):
pass
@service(as_type=IEmailService)
class SmtpEmailService(IEmailService):
async def send_email(self, to: str, subject: str, body: str):
# SMTP implementation
pass
# Inject using the interface
@service
class NotificationService:
def __init__(self, email_service: Autowired[IEmailService]):
self.email_service = email_service
Lifecycle Hooks
Components can define lifecycle methods:
from fastapi_autowire import service
@service
class DatabaseService:
async def post_construct(self):
"""Called after all dependencies are injected"""
print("Initializing database connection...")
# Setup connection pool, etc.
async def shutdown(self):
"""Called during application shutdown"""
print("Closing database connection...")
# Cleanup resources
Lifecycle execution order:
- Startup: All components are instantiated →
post_construct()called on each - Shutdown:
shutdown()orclose()called in reverse order
Advanced Usage
Accessing the Application Context
from fastapi import Request
from fastapi_autowire.core import AppContext
@app.get("/debug/services")
async def list_services(request: Request):
context = request.app.state.app_context
# Access registered services
user_service = context.get(UserService)
return {"service": type(user_service).__name__}
Manual Component Retrieval
from fastapi_autowire.core import AppContext
# After app startup
context = AppContext.current()
user_service = context.get(UserService)
Logging & Debugging
fastapi-autowire comes with a built-in logging system to help you monitor the dependency resolution process and troubleshoot any issues during startup.
The library uses a dedicated logger named fastapi_autowire.
Enabling Logs
By default, the library is silent (using logging.NullHandler). You can enable logs by configuring the level in your application:
import logging
import sys
# Standard logging configuration
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
# Enable detailed debug logs for the DI container
logging.getLogger("fastapi_autowire").setLevel(logging.DEBUG)
How It Works
- Registration Phase: Decorators like
@serviceregister components in a global registry - Resolution Phase: During app startup, the dependency resolver:
- Analyzes constructor signatures
- Builds a dependency graph
- Detects circular dependencies
- Instantiates components in the correct order
- Injection Phase: Components are injected via:
- Constructor parameters (for other components)
- FastAPI's
Depends()mechanism (for route handlers)
- Lifecycle Phase:
post_construct()hooks are called after instantiation
Visualizing the Process
sequenceDiagram
autonumber
participant Code as 📄 Python Files
participant Registry as 🗂️ Global Registry
participant FastAPI as ⚡ FastAPI (Lifespan)
participant Resolver as 🧠 Dependency Resolver
participant Container as 📦 AppContext (Container)
participant Endpoint as 🛣️ Route Handler
note over Code, Registry: Phase 1: Registration (Import Time)
Code->>Registry: Decorators execute (@component, @service...)
Registry-->>Registry: Store class references and types
note over FastAPI, Container: Phase 2: The "Magic" Startup (Resolution & Instantiation)
FastAPI->>Resolver: Application starts, lifespan triggers resolution
Resolver->>Registry: Fetch all registered classes
Resolver-->>Resolver: Analyze __init__ signatures & build dependency graph (Topological Sort)
loop Instantiate in Order
Resolver-->>Resolver: Create instances, injecting dependencies recursively
end
Resolver->>Container: Store fully constructed singletons
Resolver->>Container: Trigger async post_construct() hooks
note over FastAPI, Endpoint: Phase 3: Runtime Injection (Request Time)
[Client]->>FastAPI: Incoming HTTP Request
FastAPI->>Endpoint: Match route handler
Endpoint->>Container: Autowired[T] requests instance of T
Container-->>Endpoint: Returns pre-instantiated singleton (O(1) lookup)
Endpoint-->>[Client]: Send response
The diagram above illustrates the three stages of fastapi-autowire:
-
Registration Phase (Import Time): As Python loads your code, the decorators (@service, @repository) execute and register your classes in a central registry. No objects are created yet.
-
Startup Phase (The "Magic"): When FastAPI starts, the lifespan kicks in. The Resolver analyzes the dependency graph of all registered classes, determines the correct order, and instantiates them one by one, injecting necessary dependencies. The final singletons are stored in the AppContext container.
-
Runtime Phase (Request Time): When a request hits an endpoint using Autowired[T], FastAPI fetches the already-instantiated singleton directly from the AppContext in O(1) time. Zero overhead during requests.
API Reference
Decorators
@component(as_type=None)- Register a component@service(as_type=None)- Register a service (alias of@component)@repository(as_type=None)- Register a repository (alias of@component)@configuration(as_type=None)- Register a configuration (alias of@component)@provider(as_type=None)- Register a provider (alias of@component)
Parameters:
as_type: Optional type to register the component as (for interface-based registration)
Types
Autowired[T]- Generic type for dependency injectionservice: Autowired[UserService]
Functions
lifespan(app: FastAPI)- Async context manager for FastAPI lifespan events
Classes
AppContext- Application context holding all registered componentscurrent()- Get the current context instanceget(component_type)- Retrieve a component by type
Requirements
- Python 3.9+
- FastAPI 0.100.0+
- typing-extensions 4.0.0+ (for Python < 3.10)
Development
# Clone the repository
git clone https://github.com/leoroop/fastapi-autowire.git
cd fastapi-autowire
# Install dependencies with uv
uv sync
# Run tests
uv run pytest
# Run linter
uv run ruff check .
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Inspired by Spring Framework's dependency injection system, adapted for Python and FastAPI.
Support
If you encounter any issues or have questions, please open an issue on GitHub.
Project details
Release history Release notifications | RSS feed
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_autowire-0.1.1.tar.gz.
File metadata
- Download URL: fastapi_autowire-0.1.1.tar.gz
- Upload date:
- Size: 35.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5444d94e29c3a72a79ff6424c3ebd554ee24802b899e0001f4d75ff41755ec4
|
|
| MD5 |
0ee7907dc34fcf8f40899f94ff295032
|
|
| BLAKE2b-256 |
da13e9c4135c1fb3d5cabd1bec0cb7ffc2bef4510c82ee41f7a178c9017a58f1
|
Provenance
The following attestation bundles were made for fastapi_autowire-0.1.1.tar.gz:
Publisher:
release.yml on leoroop/fastapi-autowire
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_autowire-0.1.1.tar.gz -
Subject digest:
a5444d94e29c3a72a79ff6424c3ebd554ee24802b899e0001f4d75ff41755ec4 - Sigstore transparency entry: 779856185
- Sigstore integration time:
-
Permalink:
leoroop/fastapi-autowire@2ed9376f7df747d27584e41effe7b58ff764828d -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/leoroop
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2ed9376f7df747d27584e41effe7b58ff764828d -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastapi_autowire-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_autowire-0.1.1-py3-none-any.whl
- Upload date:
- Size: 11.4 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 |
62370a3b855b6db4e344513031bd3f600267e7bef787a6dcaf2b96377454215c
|
|
| MD5 |
bf4aadffa176a2c402807395c84e0d82
|
|
| BLAKE2b-256 |
131b745a4a1800155b94b168948f1df5b4cf73e2e8d0611427f5ce99c5b73c0f
|
Provenance
The following attestation bundles were made for fastapi_autowire-0.1.1-py3-none-any.whl:
Publisher:
release.yml on leoroop/fastapi-autowire
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_autowire-0.1.1-py3-none-any.whl -
Subject digest:
62370a3b855b6db4e344513031bd3f600267e7bef787a6dcaf2b96377454215c - Sigstore transparency entry: 779856186
- Sigstore integration time:
-
Permalink:
leoroop/fastapi-autowire@2ed9376f7df747d27584e41effe7b58ff764828d -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/leoroop
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2ed9376f7df747d27584e41effe7b58ff764828d -
Trigger Event:
push
-
Statement type: