Skip to main content

ASP.NET Core-style dependency injection for FastAPI

Project description

fastapi-app-builder

PyPI version Python versions Downloads License

Buy Me A Coffee

ASP.NET Core-style dependency injection for FastAPI - clean controllers, builder pattern, zero boilerplate.

Features

  • Clean controllers - No Depends(), Injected[], or Provide[] - just type hints
  • Standard FastAPI - Use standard APIRouter - no custom router classes needed
  • Builder pattern - Familiar AppBuilder for 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


Download files

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

Source Distribution

fastapi_app_builder-0.1.0b1.tar.gz (18.1 kB view details)

Uploaded Source

Built Distribution

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

fastapi_app_builder-0.1.0b1-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

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

Hashes for fastapi_app_builder-0.1.0b1.tar.gz
Algorithm Hash digest
SHA256 3647a7bc9c3f3c9d61aeb01b421ffc95fdf1e49aa98adb9c015c22a900601ceb
MD5 5bbc73f1b1ad23dcd2d04624f55244c3
BLAKE2b-256 2eac85c796281787033eb7e430ebc3fc65cbd9894ab342484b3b932df207ea9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_app_builder-0.1.0b1.tar.gz:

Publisher: publish.yml on Saurus119/fastapi-builder

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_app_builder-0.1.0b1-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_app_builder-0.1.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 c413f409b42482a4328cf37e02a10b1c0aef103ef4438a97b7c253f08899286a
MD5 96c9ded68b228b1ec3bbc69bc1b1b8fc
BLAKE2b-256 e7cb8de1ed1a287cb84f98cfa37de04fb750be754390340fed92a768457a22d6

See more details on using hashes here.

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

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