
Security News
NIST Under Federal Audit for NVD Processing Backlog and Delays
As vulnerability data bottlenecks grow, the federal government is formally investigating NIST’s handling of the National Vulnerability Database.
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.
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.
@inject
, @inject_method
, and @inject_class
.Python>=3.10
Starlette>=0.38.0
Pydantic>=1.10.21
You can simply install starlette-di from PyPI:
pip install starlette-di
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()
Use a ServiceCollection
to register services with different lifetimes:
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)
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.Route
class for endpoints that do not expect the request object to be passed, you can set thepass_request
argument 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.
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),
]
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
.
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_NAME
constant:
from starlette_di.definitions import SERVICE_PROVIDER_ARG_NAME request.scope[SERVICE_PROVIDER_ARG_NAME] # <starlette_di.service_provider.ScopedServiceProvider object at 0x00000...>
Find the full tutorial example here.
See the contribution guidelines.
This project is licensed under the MIT License. See the LICENSE file for details.
If you find this project useful, give it a ⭐ on GitHub!
FAQs
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.
We found that starlette-di demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
As vulnerability data bottlenecks grow, the federal government is formally investigating NIST’s handling of the National Vulnerability Database.
Research
Security News
Socket’s Threat Research Team has uncovered 60 npm packages using post-install scripts to silently exfiltrate hostnames, IP addresses, DNS servers, and user directories to a Discord-controlled endpoint.
Security News
TypeScript Native Previews offers a 10x faster Go-based compiler, now available on npm for public testing with early editor and language support.