Decorate super simple
@injectableon your FastAPI services.
Source Code: https://github.com/jymchng/fastapi-service
Effortless Dependency Injection for FastAPI.
fastapi-service is a lightweight, zero-boilerplate dependency injection library designed specifically for FastAPI. It bridges the gap between complex enterprise DI containers and FastAPI's native Depends system, making your code cleaner, more testable, and easier to migrate.
FastAPI's dependency injection is powerful, but as your application grows, managing deeply nested dependencies can become verbose. fastapi-service solves this by introducing a simple @injectable decorator that handles the wiring for you, while staying 100% compatible with standard FastAPI patterns.
- Simple APIs: Just add
@injectableto your classes. No configuration files, no complex setup. - Deep FastAPI Integration: Works directly with
Depends(). No need to learn a new framework. - Support for Plain Classes: Inject classes you didn't write or don't want to decorate (like third-party libraries).
- Gradual Migration: Adopt it incrementally. Perfect for refactoring legacy codebases without rewriting everything at once.
PIP
pip install fastapi-serviceUV
uv add fastapi-servicePOETRY
poetry add fastapi-serviceSimply decorate your classes with @injectable. Dependencies defined in __init__ are automatically resolved.
from fastapi_service import injectable
@injectable
class DatabaseService:
def get_connection(self):
return "Database Connection"
@injectable
class UserService:
# DatabaseService is automatically injected!
def __init__(self, db: DatabaseService):
self.db = db
def get_user(self, user_id: int):
conn = self.db.get_connection()
return {"id": user_id, "name": "John Doe", "connection": conn}Use your services in routes exactly like you would with standard FastAPI dependencies.
from fastapi import FastAPI, Depends
from .services import UserService
app = FastAPI()
@app.get("/users/{user_id}")
def read_user(user_id: int, service: UserService = Depends(UserService)):
return service.get_user(user_id)You don't always have control over the classes you need to use. Whether it's a legacy utility class you can't touch yet, or a client from a third-party library (like httpx or boto3), fastapi-service has you covered.
You do not need to put @injectable on everything.
If an @injectable service depends on a plain class, the library will automatically attempt to instantiate and inject it for you.
# --- legacy_utils.py ---
# This is a plain class. No decorators.
# Maybe it comes from a library you can't edit!
class SimpleLogger:
def log(self, message: str):
print(f"[Legacy Log]: {message}")
# --- services.py ---
from fastapi_service import injectable
from .legacy_utils import SimpleLogger
@injectable
class OrderService:
# SimpleLogger is NOT decorated, but it is injected automatically.
def __init__(self, logger: SimpleLogger):
self.logger = logger
def create_order(self, item: str):
self.logger.log(f"Order created for {item}")This feature is incredibly powerful for integrating:
- Third-party clients (e.g., SDKs that don't use injection).
- Legacy code during a gradual refactor.
- Data classes or simple utilities.
One of the biggest strengths of fastapi-service is its ability to fit into existing projects without breaking changes. You don't need to convert your entire codebase overnight.
Imagine you have a standard class that isn't using any dependency injection.
# legacy.py
class LegacyEmailSender:
def send(self, msg):
print(f"Sending {msg}")You can write a new service using @injectable that depends on the legacy class. Because of the Plain Object Injection feature (see above), you don't even need to modify legacy.py!
from fastapi_service import injectable
from .legacy import LegacyEmailSender
@injectable
class NotificationService:
# LegacyEmailSender is injected automatically without a decorator
def __init__(self, sender: LegacyEmailSender):
self.sender = sender
def notify(self, message: str):
self.sender.send(message)When you are ready, you can add @injectable to the legacy class if you want it to manage its own dependencies, but it is strictly optional.
fastapi-service is built on top of FastAPI's dependency system, not instead of it. This means you can mix @injectable services with standard FastAPI Depends functions.
from fastapi import Header, Depends
from fastapi_service import injectable
# A standard FastAPI dependency function
def get_user_agent(user_agent: str = Header(default=None)):
return user_agent
@injectable
class AnalyticsService:
# Injecting a standard FastAPI dependency into a service class!
def __init__(self, user_agent: str = Depends(get_user_agent)):
self.user_agent = user_agent
def log_access(self):
print(f"Access from: {self.user_agent}")fastapi-service provides robust scope management to ensure your application's lifecycle is handled correctly. We support two primary scopes:
Scopes.TRANSIENT(Default): A new instance is created for every injection. This is standard FastAPI behavior.Scopes.SINGLETON: The same instance is shared across the entire application lifetime.
A common pitfall in dependency injection is injecting a short-lived object (Transient) into a long-lived object (Singleton). This causes the short-lived object to "live forever" inside the singleton, often leading to stale database sessions or thread-safety issues.
fastapi-service validates your dependency graph at runtime. When a service is requested, the library checks the entire chain. If you attempt to inject a TRANSIENT service into a SINGLETON service, it will detect the mismatch and raise an error, preventing unstable behavior.
from fastapi_service import injectable, Scopes
# β ERROR: You cannot inject this Transient service...
@injectable(scope=Scopes.TRANSIENT)
class DatabaseSession:
pass
# ...into this Singleton service.
@injectable(scope=Scopes.SINGLETON)
class GlobalCache:
def __init__(self, session: DatabaseSession):
self.session = sessionCorrect Usage: Singletons should only depend on other Singletons or stateless configurations.
@injectable(scope=Scopes.SINGLETON)
class ConnectionPool:
pass
@injectable(scope=Scopes.SINGLETON)
class UserRepository:
def __init__(self, pool: ConnectionPool):
self.pool = poolfastapi-service is designed to be a structural analyzer that leverages Python's native metaprogramming capabilities to prepare your classes for FastAPI's dependency resolution system.
Here is the step-by-step breakdown of the injection lifecycle:
When you decorate a class with @injectable, the library utilizes Python's inspect module and typing.get_type_hints to analyze the __init__ constructor. It registers the class and its type hints, preparing them for future resolution.
This is the core mechanism that enables integration with FastAPI. fastapi-service dynamically generates a factory function for each service.
The library constructs a new inspect.Signature for this factory, programmatically inserting fastapi.Depends(...) into the default values of the function parameters.
Essentially, it automates the translation from:
# Your clean code
class UserService:
def __init__(self, db: Database): ...To the verbose definition FastAPI expects:
# What FastAPI sees internally
def user_service_factory(db: Database = Depends(database_factory)):
return UserService(db=db)Currently, validation occurs dynamically at runtime during the dependency resolution phase.
- Scope Safety: When a dependency is instantiated, the library verifies that no
Scopes.SINGLETONservice is attempting to hold onto aScopes.TRANSIENTservice. - Cycle Detection: Standard Python recursion limits and dependency resolution mechanics naturally prevent infinite loops.
Note: Future versions of fastapi-service aim to move these validations to build-time (startup) to catch configuration errors even earlier.
Because the library translates your structure into standard FastAPI dependency chains, you retain all the performance benefits of FastAPI's asynchronous execution model. The "magic" happens only during the wiring phase; the execution is pure FastAPI.
Contributions are welcome! Please read our contributing guidelines to get started.
This project is licensed under the terms of the MIT license.