Skip to content

Commit 028e403

Browse files
author
Dairo Mosquera
committed
docs: update README.md
1 parent 23028b1 commit 028e403

File tree

1 file changed

+124
-59
lines changed

1 file changed

+124
-59
lines changed

README.md

Lines changed: 124 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,44 @@
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
6780
from abc import ABC, abstractmethod
6881

69-
7082
class 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
102110
from starlette_di import ServiceCollection
@@ -108,7 +116,7 @@ services.add_transient(IGreeter, Greeter)
108116
provider = services.build_provider()
109117
```
110118

111-
This is the same for factory functions:
119+
Using a factory function:
112120

113121
```python
114122
def greeter_factory() -> IGreeter:
@@ -117,40 +125,41 @@ def greeter_factory() -> IGreeter:
117125
services.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
123131
the 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

132140
Inject the service into an endpoint function using the `@inject` decorator:
133141

134142
```python
135143
from starlette.requests import Request
136144
from starlette.responses import JSONResponse
145+
137146
from starlette_di import inject
138147

139148
@inject
140149
async 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

146155
Inject the service into an endpoint method using the `@inject_method` decorator:
147156

148157
```python
149158
from starlette.requests import Request
150159
from starlette.endpoints import HTTPEndpoint
151160
from starlette.responses import JSONResponse
152-
from starlette_di import inject_method
153161

162+
from starlette_di import inject_method
154163

155164
class 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

177188
Inject the service into an endpoint class using the `@inject_class` decorator:
178189

179190
```python
180191
from 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
185197
class 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

202289
Use the `DependencyInjectionMiddleware` to handle dependency injection.
203290

204291
This 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

207294
Pass the service provider built in [here](#2-create-a-service-collection) to
208295
the `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)
267332
file 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

Comments
 (0)