FastAPI + Dishka with providers being first-class citizens
Project description
๐ fastapi-dishka
๐ฝ๏ธ Serve your FastAPI dependencies with style! A delightful integration between FastAPI and Dishka that makes dependency injection feel like a five-star dining experience.
โจ What is this magic?
fastapi-dishka bridges the gap between FastAPI and Dishka, bringing you:
- ๐ Auto-registration - Routers and middleware register themselves like magic
- ๐ฏ Provider-first design - Your providers are first-class citizens
- ๐งฉ Seamless integration - Works with existing FastAPI and Dishka code
- ๐ Zero boilerplate - Less setup, more building awesome stuff
- ๐ Type-safe - Full type hints and mypy support
- โก High performance - Built on FastAPI and Dishka's solid foundations
๐ ๏ธ Installation
Get started in seconds:
pip install fastapi-dishka
Or if you're feeling fancy with poetry:
poetry add fastapi-dishka
๐ฌ Quick Start
Here's how easy it is to get rolling:
from dishka import Scope, provide, FromDishka
from fastapi_dishka import App, APIRouter, provide_router, Provider
# ๐ฆ Create your service
class GreetingService:
def greet(self, name: str) -> str:
return f"Hello, {name}! ๐"
# ๐ฃ๏ธ Create your router
router = APIRouter(prefix="/api")
@router.get("/greet/{name}")
async def greet_endpoint(name: str, service: FromDishka[GreetingService]) -> dict:
return {"message": service.greet(name)}
# ๐ญ Create your provider
class AppProvider(Provider):
scope = Scope.APP
# ๐ฏ Auto-register the router
greeting_router = provide_router(router)
# ๐ Provide your services
greeting_service = provide(GreetingService, scope=Scope.APP)
# ๐ Launch your app
app = App("My Awesome API", "1.0.0", AppProvider())
if __name__ == "__main__":
app.start_sync() # ๐ฅ Your API is now running!
That's it! Your API is running with auto-registered routes and dependency injection. ๐
๐ญ Features & Examples
๐ Auto-Router Registration
Say goodbye to manually registering every router:
from fastapi_dishka import provide_router, Provider
class MyProvider(Provider):
# โจ These routers register themselves automatically
users_router = provide_router(users_router)
posts_router = provide_router(posts_router)
comments_router = provide_router(comments_router)
๐ก๏ธ Middleware with Dependency Injection
Create powerful middleware that can inject dependencies:
from fastapi_dishka import Middleware, provide_middleware, Provider
class AuthMiddleware(Middleware):
async def dispatch(self, request, call_next):
# ๐ Inject services right into your middleware!
auth_service = await self.get_dependency(request, AuthService)
if not auth_service.is_authenticated(request):
return JSONResponse({"error": "Unauthorized"}, status_code=401)
return await call_next(request)
class SecurityProvider(Provider):
scope = Scope.APP
auth_service = provide(AuthService, scope=Scope.APP)
# ๐ก๏ธ Auto-register middleware with DI support
auth_middleware = provide_middleware(AuthMiddleware)
๐๏ธ Multiple Providers
Organize your code with multiple providers:
from fastapi_dishka import Provider
# ๐ค User-related stuff
class UserProvider(Provider):
scope = Scope.APP
user_router = provide_router(user_router)
user_service = provide(UserService, scope=Scope.APP)
# ๐ Post-related stuff
class PostProvider(Provider):
scope = Scope.APP
post_router = provide_router(post_router)
post_service = provide(PostService, scope=Scope.APP)
# ๐ Combine them all
app = App("Blog API", "2.0.0", UserProvider(), PostProvider())
๐ Server Management
Full control over your server lifecycle:
# ๐ฅ Blocking mode (great for production)
app.start_sync(host="0.0.0.0", port=8080)
# ๐งต Non-blocking mode (perfect for testing)
app.start_sync(blocking=False, port=8081)
# ... do other stuff ...
app.stop() # ๐ Graceful shutdown
# โก Async mode
await app.start(host="127.0.0.1", port=8082)
๐๏ธ Architecture
fastapi-dishka follows a provider-first design:
๐ฆ Your App
โโโ ๐ญ Providers (define what you have)
โ โโโ ๐ฃ๏ธ Router providers (auto-register routes)
โ โโโ ๐ก๏ธ Middleware providers (auto-register middleware)
โ โโโ ๐ Service providers (your business logic)
โโโ ๐ Auto-registration (happens magically)
โโโ ๐ FastAPI App (ready to serve)
๐๏ธ Provider Options
fastapi-dishka gives you flexibility in how you define your providers. You have two options:
Option 1: Use fastapi-dishka Provider (Recommended)
from fastapi_dishka import Provider
class MyProvider(Provider):
scope = Scope.APP
# Your provider methods here...
This is the recommended approach as it's specifically designed for fastapi-dishka integration.
Option 2: Use dishka Provider with fastapi-dishka metaclass
from dishka import Provider
from fastapi_dishka import FastAPIDishkaProviderMeta
class MyProvider(Provider, metaclass=FastAPIDishkaProviderMeta):
scope = Scope.APP
# Your provider methods here...
This approach allows you to use dishka's Provider directly while still getting fastapi-dishka's auto-registration features through the metaclass.
Both approaches provide the same functionality - choose the one that fits your project's needs! ๐ฏ
๐งช Testing
Testing is a breeze with multiple patterns and full async support! Let's start with the classic hello world test:
import pytest
from fastapi.testclient import TestClient
from dishka import Scope, provide, FromDishka
from fastapi_dishka import App, APIRouter, provide_router, start_test, stop_test, test, Provider
class GreetingService:
def greet(self, name: str) -> str:
return f"Hello, {name}! ๐"
hello_router = APIRouter()
@hello_router.get("/hello/{name}")
async def hello_endpoint(name: str, service: FromDishka[GreetingService]) -> dict:
return {"message": service.greet(name)}
class HelloProvider(Provider):
scope = Scope.APP
greeting_router = provide_router(hello_router)
greeting_service = provide(GreetingService, scope=Scope.APP)
๐ฏ Pattern 1: Context Manager (Recommended!)
The cleanest and most convenient way to test:
@pytest.mark.asyncio
async def test_hello_world_with_context_manager():
"""The cleanest way to test - using the context manager! ๐ฏ"""
app = App("Hello World API", "1.0.0", HelloProvider())
# ๐ฏ Ultra-clean testing with context manager
async with test(app) as test_app:
client = TestClient(test_app.app)
response = client.get("/hello/World")
assert response.status_code == 200
data = response.json()
assert data["message"] == "Hello, World! ๐"
# ๐งน Cleanup happens automatically!
๐ง Pattern 2: Manual Start/Stop
For more control over the server lifecycle:
@pytest.mark.asyncio
async def test_hello_world():
"""Manual server management with start_test/stop_test."""
app = App("Hello World API", "1.0.0", HelloProvider())
try:
# ๐ Use start_test() for clean async server startup
await start_test(app, port=9999)
client = TestClient(app.app)
response = client.get("/hello/World")
assert response.status_code == 200
data = response.json()
assert data["message"] == "Hello, World! ๐"
finally:
# ๐งน Use stop_test() for clean async server shutdown
await stop_test(app)
๐ญ Which Pattern to Choose?
- ๐ฏ Context Manager: Perfect for most tests, cleanest syntax, automatic cleanup
- ๐ง Start/Stop: Use when you need custom server lifecycle management or multiple test phases
Both patterns handle provider reuse correctly, so you can use the same providers across multiple tests! ๐
๐ค Contributing
We love contributions! Here's how to get started:
๐ Quick Setup
# ๐ฅ Clone the repo
git clone https://github.com/NSXBet/fastapi-dishka.git
cd fastapi-dishka
# ๐ Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# ๐ฆ Install dependencies
pip install -e ".[dev]"
๐งช Running Tests
We maintain 90%+ test coverage (we're a bit obsessed ๐ ):
# ๐โโ๏ธ Run all tests
make test
# ๐ Check coverage
make coverage
# ๐ Lint your code
make lint
# โจ Format your code
make format
๐ฏ Development Standards
- โ Type Safety: We love type hints and use mypy
- ๐งช Test Coverage: Keep it above 90%
- ๐ Documentation: Update docs for new features
- ๐จ Code Style: We use ruff and flake8
- ๐ Provider-First: Make providers first-class citizens
๐ก Ideas for Contributions
- ๐ Additional integrations (SQLAlchemy, Redis, etc.)
- ๐ More examples and tutorials
- ๐ Bug fixes and performance improvements
- ๐ Documentation improvements
- ๐งช More test coverage (can we hit 99%? ๐)
๐ Issues & Questions
Found a bug? Have a question? Want to suggest a feature?
- ๐ Report bugs
- ๐ก Request features
- โ Ask questions
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- ๐ FastAPI - For making APIs fun again
- ๐ฝ๏ธ Dishka - For elegant dependency injection
- โค๏ธ All our contributors and users
โญ Show Your Support
If you like this project, please consider giving it a star! It helps others discover fastapi-dishka and motivates us to keep improving it.
Made with โค๏ธ and lots of โ
Happy coding! ๐
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_dishka-0.2.3.tar.gz.
File metadata
- Download URL: fastapi_dishka-0.2.3.tar.gz
- Upload date:
- Size: 23.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ccb4606eebeb69f496e950a1856d5dbd483fd1b8ba9dfcbf3260c23e6eba2aa
|
|
| MD5 |
ea6fe2807893dbd8c52ecdf0a1a3f71e
|
|
| BLAKE2b-256 |
22881acd4b5ed8bc5eac9820e0f41681f01742454a6247f2a1e6a887ed1e3958
|
Provenance
The following attestation bundles were made for fastapi_dishka-0.2.3.tar.gz:
Publisher:
pypi.yaml on NSXBet/fastapi-dishka
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_dishka-0.2.3.tar.gz -
Subject digest:
0ccb4606eebeb69f496e950a1856d5dbd483fd1b8ba9dfcbf3260c23e6eba2aa - Sigstore transparency entry: 246038916
- Sigstore integration time:
-
Permalink:
NSXBet/fastapi-dishka@c5011cb33ee2ad5e0b4d8b047b753780fcfb4a70 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/NSXBet
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@c5011cb33ee2ad5e0b4d8b047b753780fcfb4a70 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastapi_dishka-0.2.3-py3-none-any.whl.
File metadata
- Download URL: fastapi_dishka-0.2.3-py3-none-any.whl
- Upload date:
- Size: 13.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8480b08ece9e16ef289336cfe3de5ce71ce5064fa33b17dc6b3f688a0094b243
|
|
| MD5 |
95b7ef6d6542e4eddbefce1c49d826eb
|
|
| BLAKE2b-256 |
76c07321fa87ff09780e29346a63c714cd792f027f64104f1be018a9f5f83fb8
|
Provenance
The following attestation bundles were made for fastapi_dishka-0.2.3-py3-none-any.whl:
Publisher:
pypi.yaml on NSXBet/fastapi-dishka
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_dishka-0.2.3-py3-none-any.whl -
Subject digest:
8480b08ece9e16ef289336cfe3de5ce71ce5064fa33b17dc6b3f688a0094b243 - Sigstore transparency entry: 246038917
- Sigstore integration time:
-
Permalink:
NSXBet/fastapi-dishka@c5011cb33ee2ad5e0b4d8b047b753780fcfb4a70 -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/NSXBet
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@c5011cb33ee2ad5e0b4d8b047b753780fcfb4a70 -
Trigger Event:
push
-
Statement type: