A dependency injection library for Starlette. It supports Scoped, Transient, and Singleton lifetimes, route parameter and request body injection via Pydantic, and seamless integration with Starlette using decorators and middleware.
Project description
Starlette DI
Starlette DI is a dependency injection library for Starlette applications. It simplifies dependency management by allowing services to be injected using Scoped, Transient, and Singleton lifetimes (similar to .NET Core). Also, enables automatic injection of route parameters, and request bodies using Pydantic models, making API development more efficient, and structured.
Table of Contents
Features
- Scoped, Transient, and Singleton: Dependency injection with three service lifetimes, similar to .NET Core.
- Service Injection: Supports injecting services into functions, methods, and endpoint classes in Starlette.
- Route Parameter Injection: Automatically extracts URL parameters in controllers.
- Request Body Injection: Maps JSON request body data directly to Pydantic models.
- Dependency Injection Middleware: Provides a middleware layer to manage dependency injection throughout the request lifecycle.
- Pydantic Compatibility: Leverages Pydantic for data validation, and conversion.
- Decorators for Endpoints: Simplifies injection with
@inject,@inject_method, and@inject_class.
Requirements
Python>=3.10Starlette>=0.38.0Pydantic>=1.10.21
Installation
You can simply install starlette-di from PyPI:
pip install starlette-di
Tutorial
1. Create a service
Define a service that can be injected:
from abc import ABC, abstractmethod
class IGreeter(ABC):
@abstractmethod
def greet(self) -> str: ...
class Greeter(IGreeter):
def greet(self) -> str:
return 'Hello!'
Alternatively, use a factory function:
def greeter_factory() -> IGreeter:
return Greeter()
2. Configure dependency injection
Use a ServiceCollection to register services with different lifetimes:
- Singleton: one instance for the application lifetime.
- Scoped: one instance per request.
- Transient: new instance created each time it's requested.
Example:
from starlette_di import ServiceCollection
services = ServiceCollection()
services.add_transient(IGreeter, Greeter)
# also, services.add_scoped(IGreeter, Greeter)
# or services.add_singleton(IGreeter, Greeter)
provider = services.build_provider()
Using a factory function:
def greeter_factory() -> IGreeter:
return Greeter()
services.add_transient(IGreeter, greeter_factory)
3. Injecting services
Use the @inject, @inject_method, and @inject_class decorators to inject
the service into an endpoint function, method or class respectively.
[!WARNING] Only asynchronous endpoints can be decorated. Trying to decorate a synchronous endpoint will raise a
TypeError.
Inject into an endpoint function
Inject the service into an endpoint function using the @inject decorator:
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette_di import inject
@inject
async def greet(request: Request, greeter: IGreeter):
return JSONResponse({'message': greeter.greet()})
Inject into an endpoint method
Inject the service into an endpoint method using the @inject_method decorator:
from starlette.requests import Request
from starlette.endpoints import HTTPEndpoint
from starlette.responses import JSONResponse
from starlette_di import inject_method
class GreetEndpoint(HTTPEndpoint):
@inject_method
async def get(self, request: Request, greeter: IGreeter):
return JSONResponse({'message': greeter.greet()})
[!NOTE] If you are implementing a custom
starlette.routing.Routeclass for endpoints that do not expect the request object to be passed, you can set thepass_requestargument toFalse:from starlette.responses import JSONResponse from starlette.endpoints import HTTPEndpoint from starlette_di import inject_method class GreetEndpoint(HTTPEndpoint): @inject_method(pass_request=False) async def get(self, greeter: IGreeter): return JSONResponse({'message': greeter.greet()})
Inject into an endpoint class
Inject the service into an endpoint class using the @inject_class decorator:
from starlette.responses import JSONResponse
from starlette.endpoints import HTTPEndpoint
from starlette_di import inject_class
@inject_class
class GreetEndpoint(HTTPEndpoint):
def __init__(self, request: Request, greeter: IGreeter):
super().__init__(request)
self.greeter = greeter
async def get(self, request: Request):
return JSONResponse({'message': self.greeter.greet()})
[!WARNING] The decorated class must be a subclass of
starlette.endpoints.HTTPEndpoint. Otherwise, it will raise aTypeError. To learn more about endpoints, see the Starlette documentation.
4. Inject path params
You can inject request path parameters:
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette_di import inject
@inject
async def greet_person(self, request: Request, name: str):
return JSONResponse({'message': f'Hello {name}!'})
routes = [
Route('/greet/{name:str}', greet_person),
]
5. Inject request body
Also, you can inject the request body using Pydantic models. If there's only one Pydantic model parameter, the whole JSON body is injected. Otherwise, each parameter is extracted from the JSON body using its name.
Only one parameter:
from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse
class User(BaseModel):
name: str
age: int
@inject
async def create_user(request: Request, user: User):
return JSONResponse({'name': user.name, 'age': user.age})
# Example request
# {'name': 'Jane Doe', 'age': 25}
Two or more parameters
from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse
class User(BaseModel):
name: str
age: int
class Product(BaseModel):
name: str
price: float
@inject
async def update_product(request: Request, user: User, product: Product):
return JSONResponse({'user_name': user.name, 'product_name': product.name})
# Example request
# {
# 'user': {'name': 'Jane Doe', 'age': 25},
# 'product': {'name': 'Computer', 'price': 225.0},
# }
[!WARNING] The request body must be a JSON dict. Otherwise, it will raise a
ValueError.
6. Use the DependencyInjectionMiddleware
Use the DependencyInjectionMiddleware to handle dependency injection.
This middleware sets up the request scope for dependency injection by creating a scoped service provider, and adding it to the request scope.
Pass the service provider built in here to
the service_provider argument of the middleware:
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.routing import Route
from starlette_di import DependencyInjectionMiddleware
app = Starlette(
routes=[Route('/greet', GreetEndpoint)],
middleware=[
Middleware(DependencyInjectionMiddleware, service_provider=provider),
]
)
[!NOTE] You can access the scoped service provider from the request scope using the
SERVICE_PROVIDER_ARG_NAMEconstant:from starlette_di.definitions import SERVICE_PROVIDER_ARG_NAME request.scope[SERVICE_PROVIDER_ARG_NAME] # <starlette_di.service_provider.ScopedServiceProvider object at 0x00000...>
Full example
Find the full tutorial example here.
Contributing
See the contribution guidelines.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Support
If you find this project useful, give it a ⭐ 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 starlette_di-0.1.1.tar.gz.
File metadata
- Download URL: starlette_di-0.1.1.tar.gz
- Upload date:
- Size: 16.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f73be0de3658acd8e87e191769a9183f331d92f482a4766c4e8bd9233df0fcf
|
|
| MD5 |
3bbd66085f8989806f0e1963b0b58abc
|
|
| BLAKE2b-256 |
0f2ecaf7638e90f4797039225b27f86f75a9a4d5231566fcaf89787601cbde12
|
Provenance
The following attestation bundles were made for starlette_di-0.1.1.tar.gz:
Publisher:
publish.yml on daireto/starlette-di
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
starlette_di-0.1.1.tar.gz -
Subject digest:
2f73be0de3658acd8e87e191769a9183f331d92f482a4766c4e8bd9233df0fcf - Sigstore transparency entry: 182238602
- Sigstore integration time:
-
Permalink:
daireto/starlette-di@3abfef22b33bbf898f46bbd90897113995a74868 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/daireto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3abfef22b33bbf898f46bbd90897113995a74868 -
Trigger Event:
push
-
Statement type:
File details
Details for the file starlette_di-0.1.1-py3-none-any.whl.
File metadata
- Download URL: starlette_di-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.1 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 |
ebc64997bfabf9c1c84b8bcce5bcdcb6d3a32189f4f6a3f65f63e4b38ad3f692
|
|
| MD5 |
89194043e4060f5b184768544ab2f480
|
|
| BLAKE2b-256 |
569c5b1ccc2522f80a668c3e769771b6fbb722b45d86f4a493197a63676f285a
|
Provenance
The following attestation bundles were made for starlette_di-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on daireto/starlette-di
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
starlette_di-0.1.1-py3-none-any.whl -
Subject digest:
ebc64997bfabf9c1c84b8bcce5bcdcb6d3a32189f4f6a3f65f63e4b38ad3f692 - Sigstore transparency entry: 182238605
- Sigstore integration time:
-
Permalink:
daireto/starlette-di@3abfef22b33bbf898f46bbd90897113995a74868 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/daireto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3abfef22b33bbf898f46bbd90897113995a74868 -
Trigger Event:
push
-
Statement type: