2222 </a>
2323</p>
2424
25- A dependency injection library for Starlette.
25+ Starlette DI is a dependency injection library for Starlette applications.
26+ It simplifies dependency management by allowing services to be injected using
27+ Scoped, Transient, and Singleton lifetimes (similar to .NET Core). Also,
28+ enables automatic injection of route parameters, and request bodies using
29+ Pydantic models, making API development more efficient, and structured.
2630
2731<!-- omit in toc -->
2832## Table of Contents
33+ - [Features](#features)
2934- [Requirements](#requirements)
3035- [Installation](#installation)
3136- [Tutorial](#tutorial)
3237 - [1. Create a service](#1-create-a-service)
33- - [2. Create a service collection](#2-create-a-service-collection)
34- - [3. Inject the service](#3-inject-the-service)
35- - [3.1. Inject into an endpoint function](#31-inject-into-an-endpoint-function)
36- - [3.2. Inject into an endpoint method](#32-inject-into-an-endpoint-method)
37- - [3.3. Inject into an endpoint class](#33-inject-into-an-endpoint-class)
38- - [4. Use the DependencyInjectionMiddleware](#4-use-the-dependencyinjectionmiddleware)
39- - [5. Make a request to the endpoint](#5-make-a-request-to-the-endpoint)
38+ - [2. Configure dependency injection](#2-configure-dependency-injection)
39+ - [3. Injecting services](#3-injecting-services)
40+ - [4. Inject path params](#4-inject-path-params)
41+ - [5. Inject request body](#5-inject-request-body)
42+ - [6. Use the DependencyInjectionMiddleware](#6-use-the-dependencyinjectionmiddleware)
4043 - [Full example](#full-example)
41- - [Documentation](#documentation)
4244- [Contributing](#contributing)
4345- [License](#license)
4446- [Support](#support)
4547
48+ ## Features
49+
50+ - **Scoped, Transient, and Singleton**: Dependency injection with three service lifetimes, similar to .NET Core.
51+ - **Service Injection**: Supports injecting services into functions, methods, and endpoint classes in Starlette.
52+ - **Route Parameter Injection**: Automatically extracts URL parameters in controllers.
53+ - **Request Body Injection**: Maps JSON request body data directly to Pydantic models.
54+ - **Dependency Injection Middleware**: Provides a middleware layer to manage dependency injection throughout the request lifecycle.
55+ - **Pydantic Compatibility**: Leverages Pydantic for data validation, and conversion.
56+ - **Decorators for Endpoints**: Simplifies injection with `@inject`, `@inject_method`, and `@inject_class`.
57+
4658## Requirements
4759
4860- `Python>=3.10`
4961- `Starlette>=0.38.0`
62+ - `Pydantic>=1.10.21`
5063
5164## Installation
5265
@@ -61,12 +74,11 @@ pip install starlette-di
6174
6275### 1. Create a service
6376
64- Create a service to be injected, for example :
77+ Define a service that can be injected:
6578
6679```python
6780from abc import ABC, abstractmethod
6881
69-
7082class IGreeter(ABC):
7183 @abstractmethod
7284 def greet(self) -> str: ...
@@ -77,26 +89,22 @@ class Greeter(IGreeter):
7789 return 'Hello!'
7890```
7991
80- > [!NOTE]
81- > You can also inject a factory function:
82- > ```python
83- > def greeter_factory() -> IGreeter:
84- > return Greeter()
85- > ```
86-
87- ### 2. Create a service collection
92+ Alternatively, use a factory function:
8893
89- Create a service collection, add the service and build a service provider.
94+ ```python
95+ def greeter_factory() -> IGreeter:
96+ return Greeter()
97+ ```
9098
91- The service provider is used to resolve dependencies.
99+ ### 2. Configure dependency injection
92100
93- There are three types of services: singleton, scoped and transient :
101+ Use a `ServiceCollection` to register services with different lifetimes :
94102
95103- **Singleton**: one instance for the application lifetime.
96104- **Scoped**: one instance per request.
97105- **Transient**: new instance created each time it's requested.
98106
99- For example :
107+ Example :
100108
101109```python
102110from starlette_di import ServiceCollection
@@ -108,7 +116,7 @@ services.add_transient(IGreeter, Greeter)
108116provider = services.build_provider()
109117```
110118
111- This is the same for factory functions :
119+ Using a factory function :
112120
113121```python
114122def greeter_factory() -> IGreeter:
@@ -117,40 +125,41 @@ def greeter_factory() -> IGreeter:
117125services.add_transient(IGreeter, greeter_factory)
118126```
119127
120- ### 3. Inject the service
128+ ### 3. Injecting services
121129
122- Use the `@inject`, `@inject_method` and `@inject_class` decorators to inject
130+ Use the `@inject`, `@inject_method`, and `@inject_class` decorators to inject
123131the service into an endpoint function, method or class respectively.
124132
125133> [!WARNING]
126134> Only asynchronous endpoints can be decorated.
127135> Trying to decorate a synchronous endpoint will raise
128136> a `TypeError`.
129137
130- #### 3.1. Inject into an endpoint function
138+ ** Inject into an endpoint function**
131139
132140Inject the service into an endpoint function using the `@inject` decorator:
133141
134142```python
135143from starlette.requests import Request
136144from starlette.responses import JSONResponse
145+
137146from starlette_di import inject
138147
139148@inject
140149async def greet(request: Request, greeter: IGreeter):
141150 return JSONResponse({'message': greeter.greet()})
142151```
143152
144- #### 3.2. Inject into an endpoint method
153+ ** Inject into an endpoint method**
145154
146155Inject the service into an endpoint method using the `@inject_method` decorator:
147156
148157```python
149158from starlette.requests import Request
150159from starlette.endpoints import HTTPEndpoint
151160from starlette.responses import JSONResponse
152- from starlette_di import inject_method
153161
162+ from starlette_di import inject_method
154163
155164class GreetEndpoint(HTTPEndpoint):
156165 @inject_method
@@ -164,6 +173,8 @@ class GreetEndpoint(HTTPEndpoint):
164173> `pass_request` argument to `False`:
165174> ```python
166175> from starlette.responses import JSONResponse
176+ > from starlette.endpoints import HTTPEndpoint
177+ >
167178> from starlette_di import inject_method
168179>
169180> class GreetEndpoint(HTTPEndpoint):
@@ -172,14 +183,15 @@ class GreetEndpoint(HTTPEndpoint):
172183> return JSONResponse({'message': greeter.greet()})
173184> ```
174185
175- #### 3.3. Inject into an endpoint class
186+ ** Inject into an endpoint class**
176187
177188Inject the service into an endpoint class using the `@inject_class` decorator:
178189
179190```python
180191from starlette.responses import JSONResponse
181- from starlette_di import inject_class
192+ from starlette.endpoints import HTTPEndpoint
182193
194+ from starlette_di import inject_class
183195
184196@inject_class
185197class GreetEndpoint(HTTPEndpoint):
@@ -197,12 +209,87 @@ class GreetEndpoint(HTTPEndpoint):
197209> To learn more about endpoints, see the
198210> [Starlette documentation](https://www.starlette.io/endpoints/).
199211
200- ### 4. Use the DependencyInjectionMiddleware
212+ ### 4. Inject path params
213+
214+ You can inject request path parameters:
215+
216+ ```python
217+ from starlette.requests import Request
218+ from starlette.responses import JSONResponse
219+ from starlette.routing import Route
220+
221+ from starlette_di import inject
222+
223+ @inject
224+ async def greet_person(self, request: Request, name: str):
225+ return JSONResponse({'message': f'Hello {name}!'})
226+
227+ routes = [
228+ Route('/greet/{name:str}', greet_person),
229+ ]
230+ ```
231+
232+ ### 5. Inject request body
233+
234+ Also, you can inject the request body using
235+ [Pydantic models](https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage).
236+ If there's only one Pydantic model parameter, the whole JSON body is injected.
237+ Otherwise, each parameter is extracted from the JSON body using its name.
238+
239+ **Only one parameter**:
240+
241+ ```python
242+ from pydantic import BaseModel
243+ from starlette.requests import Request
244+ from starlette.responses import JSONResponse
245+
246+ class User(BaseModel):
247+ name: str
248+ age: int
249+
250+ @inject
251+ async def create_user(request: Request, user: User):
252+ return JSONResponse({'name': user.name, 'age': user.age})
253+
254+ # Example request
255+ # {'name': 'Jane Doe', 'age': 25}
256+ ```
257+
258+ **Two or more parameters**
259+
260+ ```python
261+ from pydantic import BaseModel
262+ from starlette.requests import Request
263+ from starlette.responses import JSONResponse
264+
265+ class User(BaseModel):
266+ name: str
267+ age: int
268+
269+ class Product(BaseModel):
270+ name: str
271+ price: float
272+
273+ @inject
274+ async def update_product(request: Request, user: User, product: Product):
275+ return JSONResponse({'user_name': user.name, 'product_name': product.name})
276+
277+ # Example request
278+ # {
279+ # 'user': {'name': 'Jane Doe', 'age': 25},
280+ # 'product': {'name': 'Computer', 'price': 225.0},
281+ # }
282+ ```
283+
284+ > [!WARNING]
285+ > The request body must be a JSON dict. Otherwise, it will raise a `ValueError`.
286+
287+ ### 6. Use the DependencyInjectionMiddleware
201288
202289Use the `DependencyInjectionMiddleware` to handle dependency injection.
203290
204291This middleware sets up the request scope for dependency injection by creating
205- a scoped service provider and adding it to the request scope.
292+ a scoped service provider, and adding it to the request scope.
206293
207294Pass the service provider built in [here](#2-create-a-service-collection) to
208295the `service_provider` argument of the middleware:
@@ -231,41 +318,19 @@ app = Starlette(
231318> # <starlette_di.service_provider.ScopedServiceProvider object at 0x00000...>
232319> ```
233320
234- ### 5. Make a request to the endpoint
235-
236- Make a request to the endpoint:
237-
238- ```python
239- from starlette.testclient import TestClient
240-
241- client = TestClient(app)
242- response = client.get('/greet')
243- print(response.json())
244- # {'message': 'Hello!'}
245- ```
246-
247321### Full example
248322
249- You can find the full example [here](example.py).
250-
251- ```
252- > python example.py
253- All tests passed!
254- ```
255-
256- ## Documentation
257-
258- Find the complete documentation [here](https://daireto.github.io/starlette-di/).
323+ Find the full tutorial example [here](example.py).
259324
260325## Contributing
261326
262- Please read the [contribution guidelines](CONTRIBUTING.md).
327+ See the [contribution guidelines](CONTRIBUTING.md).
263328
264329## License
265330
266- This project is licensed under the MIT License - see the [LICENSE](LICENSE)
331+ This project is licensed under the MIT License. See the [LICENSE](LICENSE)
267332file for details.
268333
269334## Support
270335
271- If you find this project useful, give it a ⭐ on GitHub to show your support !
336+ If you find this project useful, give it a ⭐ on GitHub!
0 commit comments