ASP.NET Core-style dependency injection for FastAPI
Project description
fastapi-app-builder
ASP.NET Core-style dependency injection for FastAPI - clean controllers, builder pattern, zero boilerplate.
Features
- Clean controllers - No
Depends(),Injected[], orProvide[]- just type hints - Standard FastAPI - Use standard
APIRouter- no custom router classes needed - Builder pattern - Familiar
AppBuilderfor application configuration - Proper lifetimes - Singleton, Scoped (per-request), and Transient
- Fail fast - Validate all dependencies at startup
- Familiar API - Mirror .NET naming conventions
Installation
pip install fastapi-app-builder
With SQLAlchemy support:
pip install fastapi-app-builder[sqlalchemy]
Quick Start
from typing import Protocol
from fastapi import APIRouter
from fastapi_app_builder import AppBuilder
# Define your service interface and implementation
class IGreetingService(Protocol):
def greet(self, name: str) -> str: ...
class GreetingService:
def greet(self, name: str) -> str:
return f"Hello, {name}!"
# Create router with clean dependency injection - no Depends() needed!
router = APIRouter(prefix="/greetings")
@router.get("/{name}")
async def greet(name: str, greeting_service: IGreetingService):
return {"message": greeting_service.greet(name)}
# Configure and build the application
builder = AppBuilder()
builder.services.add_singleton(IGreetingService, GreetingService)
builder.add_controller(router)
app = builder.build()
Using Standard APIRouter
Use FastAPI's standard APIRouter - the dependency injection works automatically. Routers can be defined in separate files and imported in any order:
# controllers/users.py
from fastapi import APIRouter
from services import IUserService
router = APIRouter(prefix="/users", tags=["Users"])
@router.get("/{user_id}")
async def get_user(user_id: int, user_service: IUserService):
# user_service is automatically injected - no Depends() needed!
return user_service.get_user(user_id)
@router.post("/")
async def create_user(data: CreateUserDto, user_service: IUserService):
return user_service.create(data)
# main.py
from fastapi_app_builder import AppBuilder
from controllers.users import router as user_router
from services import IUserRepository, UserRepository, IUserService, UserService
builder = AppBuilder()
builder.services.add_scoped(IUserRepository, UserRepository)
builder.services.add_scoped(IUserService, UserService)
builder.add_controller(user_router)
app = builder.build()
Service Lifetimes
Singleton
One instance shared across the entire application:
builder.services.add_singleton(IMyService, MyService)
Scoped
One instance per HTTP request:
builder.services.add_scoped(IMyService, MyService)
Transient
New instance every time it's requested:
builder.services.add_transient(IMyService, MyService)
Factory Registration
Register services with custom factory functions:
builder.services.add_singleton_factory(IMyService, lambda: MyService(config))
builder.services.add_scoped_factory(IMyService, create_my_service)
Nested Dependencies
Dependencies are automatically resolved via constructor injection:
class UserRepository:
pass
class UserService:
def __init__(self, user_repository: UserRepository):
self.repo = user_repository
builder.services.add_scoped(UserRepository)
builder.services.add_scoped(UserService)
# UserService will automatically receive UserRepository
Resolving Services Anywhere
Use resolve() to get services from anywhere in your code during a request:
from fastapi_app_builder import resolve
class OrderService:
def create_order(self, items: list):
# Resolve services on-demand (not just in constructors)
inventory = resolve(IInventoryService)
payment = resolve(IPaymentService)
for item in items:
inventory.reserve(item)
return payment.charge(items)
# Also works in utility functions
def get_current_user():
auth = resolve(IAuthService)
return auth.get_current_user()
Note: Prefer constructor injection when possible - it makes dependencies explicit and easier to test.
Installer Pattern
Organize service registrations with installers for better modularity:
# installers/repositories.py
from fastapi_app_builder import Services
def install_repositories(services: Services) -> None:
services.add_scoped(IUserRepository, UserRepository)
services.add_scoped(IProductRepository, ProductRepository)
# installers/services.py
def install_services(services: Services) -> None:
services.add_scoped(IUserService, UserService)
services.add_scoped(IProductService, ProductService)
# main.py
builder = AppBuilder()
builder.services.install(install_repositories).install(install_services)
You can also use builder.install() if you need access to the full builder:
def install_all(builder: AppBuilder) -> None:
builder.services.add_scoped(IUserRepository, UserRepository)
builder.with_title("My API")
builder.install(install_all)
Configuration
builder = AppBuilder()
# Configure OpenAPI docs
builder.with_title("My API")
builder.with_version("1.0.0")
builder.with_description("My awesome API")
builder.with_docs_url("/swagger")
# Configure CORS
builder.install_cors(
origins=["http://localhost:3000"],
allow_credentials=True,
)
# Disable validation (not recommended)
builder.with_validation(False)
Validation
By default, all dependencies are validated at startup:
builder.services.add_scoped(IUserService, UserService)
# If UserService depends on IUserRepository which isn't registered,
# builder.build() will raise ValidationError
Extending an Existing FastAPI App
Use extend() to add DI to an existing FastAPI app:
from fastapi import FastAPI
from fastapi_app_builder import AppBuilder
from controllers import user_router
# Create your own FastAPI instance with custom settings
app = FastAPI(
title="My API",
lifespan=my_lifespan, # Custom lifespan handler
)
# Add routes directly
@app.get("/health")
async def health():
return {"status": "ok"}
# Use builder for DI only
builder = AppBuilder()
builder.services.add_scoped(IUserService, UserService)
builder.add_controller(user_router)
builder.extend(app) # Adds DI to existing app
SQLAlchemy Integration
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from fastapi_app_builder import AppBuilder
engine = create_engine("sqlite:///./app.db")
SessionLocal = sessionmaker(bind=engine)
def install_database(builder: AppBuilder) -> None:
# Use dispose to automatically close sessions after each request
builder.services.add_scoped_factory(
Session,
factory=SessionLocal,
dispose=lambda s: s.close()
)
builder = AppBuilder()
builder.install(install_database)
The dispose parameter ensures resources are properly cleaned up when the request ends, even if an exception occurs.
API Reference
AppBuilder
The main entry point for configuring your FastAPI application.
| Method | Description |
|---|---|
services |
Property to access the DI container |
add_controller(router) |
Add a standard FastAPI APIRouter to the application |
install(installer) |
Apply an installer function |
install_cors(origins, ...) |
Configure CORS middleware |
with_title(title) |
Set application title |
with_version(version) |
Set application version |
with_description(desc) |
Set application description |
with_docs_url(url) |
Set Swagger UI URL |
with_validation(enabled) |
Enable/disable startup validation |
build() |
Build and return a new FastAPI app |
extend(app) |
Add DI to an existing FastAPI app |
Services
The dependency injection container.
| Method | Description |
|---|---|
add_singleton(interface, impl) |
Register a singleton service |
add_scoped(interface, impl) |
Register a scoped service |
add_transient(interface, impl) |
Register a transient service |
add_singleton_factory(interface, factory) |
Register singleton with factory |
add_scoped_factory(interface, factory, dispose=None) |
Register scoped with factory and optional cleanup |
add_transient_factory(interface, factory) |
Register transient with factory |
install(installer) |
Apply an installer function to register services |
is_registered(interface) |
Check if a service is registered |
resolve(interface) |
Manually resolve a service |
validate() |
Validate all registrations |
resolve()
Resolve a service from anywhere during a request:
from fastapi_app_builder import resolve
service = resolve(IMyService)
License
MIT
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_app_builder-0.1.0b1.tar.gz.
File metadata
- Download URL: fastapi_app_builder-0.1.0b1.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3647a7bc9c3f3c9d61aeb01b421ffc95fdf1e49aa98adb9c015c22a900601ceb
|
|
| MD5 |
5bbc73f1b1ad23dcd2d04624f55244c3
|
|
| BLAKE2b-256 |
2eac85c796281787033eb7e430ebc3fc65cbd9894ab342484b3b932df207ea9b
|
Provenance
The following attestation bundles were made for fastapi_app_builder-0.1.0b1.tar.gz:
Publisher:
publish.yml on Saurus119/fastapi-builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_app_builder-0.1.0b1.tar.gz -
Subject digest:
3647a7bc9c3f3c9d61aeb01b421ffc95fdf1e49aa98adb9c015c22a900601ceb - Sigstore transparency entry: 838969215
- Sigstore integration time:
-
Permalink:
Saurus119/fastapi-builder@5bdedfd5542c554c5371f5152bdb8a69e5484b37 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Saurus119
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bdedfd5542c554c5371f5152bdb8a69e5484b37 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file fastapi_app_builder-0.1.0b1-py3-none-any.whl.
File metadata
- Download URL: fastapi_app_builder-0.1.0b1-py3-none-any.whl
- Upload date:
- Size: 23.9 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 |
c413f409b42482a4328cf37e02a10b1c0aef103ef4438a97b7c253f08899286a
|
|
| MD5 |
96c9ded68b228b1ec3bbc69bc1b1b8fc
|
|
| BLAKE2b-256 |
e7cb8de1ed1a287cb84f98cfa37de04fb750be754390340fed92a768457a22d6
|
Provenance
The following attestation bundles were made for fastapi_app_builder-0.1.0b1-py3-none-any.whl:
Publisher:
publish.yml on Saurus119/fastapi-builder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_app_builder-0.1.0b1-py3-none-any.whl -
Subject digest:
c413f409b42482a4328cf37e02a10b1c0aef103ef4438a97b7c253f08899286a - Sigstore transparency entry: 838969263
- Sigstore integration time:
-
Permalink:
Saurus119/fastapi-builder@5bdedfd5542c554c5371f5152bdb8a69e5484b37 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Saurus119
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bdedfd5542c554c5371f5152bdb8a69e5484b37 -
Trigger Event:
workflow_dispatch
-
Statement type: