p2d: add Dependency Injection tutorial — basic, auth, DB, class-based, scope, nesting

swadhinbiswas committed 3 months ago
3bb94a5
1 parent 949b1c7
commit 3bb94a5
1 file changed
+180 -0
▼docs_site/src/content/docs/tutorials/dependency-injection.md
+180-0
1
+---
2
+title: Dependency Injection
3
+description: Use Depends to inject reusable components into your route handlers.
4
+---
5
+
6
+Dependency Injection (DI) lets you declare reusable components that are automatically resolved and injected into your route handlers. JustAPI's DI system mirrors FastAPI's, making migration seamless.
7
+
8
+## Basic Dependency
9
+
10
+A dependency is any callable that returns a value:
11
+
12
+```python
13
+from justapi import JustAPIApp, Depends
14
+
15
+app = JustAPIApp()
16
+
17
+
18
+def common_params(q: str | None = None, skip: int = 0, limit: int = 100):
19
+ return {"q": q, "skip": skip, "limit": limit}
20
+
21
+
22
+@app.get("/items/")
23
+def list_items(request, params: dict = Depends(common_params)):
24
+ return {"endpoint": "items", **params}
25
+
26
+
27
+@app.get("/users/")
28
+def list_users(request, params: dict = Depends(common_params)):
29
+ return {"endpoint": "users", **params}
30
+```
31
+
32
+```bash
33
+curl "http://127.0.0.1:8000/items/?skip=10&limit=5"
34
+# Output: {"endpoint":"items","q":null,"skip":10,"limit":5}
35
+```
36
+
37
+## Authentication Dependency
38
+
39
+Dependencies can raise `HTTPException` to reject requests:
40
+
41
+```python
42
+from justapi import JustAPIApp, Depends, HTTPException, Header
43
+
44
+app = JustAPIApp()
45
+
46
+
47
+def verify_token(authorization: str = Header(...)):
48
+ if not authorization.startswith("Bearer "):
49
+ raise HTTPException(status_code=401, detail="Invalid authorization header")
50
+ token = authorization.removeprefix("Bearer ")
51
+ if token != "secret-token":
52
+ raise HTTPException(status_code=401, detail="Invalid token")
53
+ return {"user_id": 42, "role": "admin"}
54
+
55
+
56
+@app.get("/protected")
57
+def protected_route(request, user: dict = Depends(verify_token)):
58
+ return {"message": "Access granted", "user": user}
59
+```
60
+
61
+```bash
62
+curl http://127.0.0.1:8000/protected
63
+# Output: {"detail":"Invalid authorization header"}
64
+
65
+curl http://127.0.0.1:8000/protected \
66
+ -H "Authorization: Bearer secret-token"
67
+# Output: {"message":"Access granted","user":{"user_id":42,"role":"admin"}}
68
+```
69
+
70
+## Database Session Dependency
71
+
72
+```python
73
+def get_db(request):
74
+ db = app.db
75
+ try:
76
+ yield db
77
+ finally:
78
+ pass # Connection pooling handles cleanup
79
+
80
+
81
+@app.get("/users")
82
+def list_users(request, db=Depends(get_db)):
83
+ return db.query("SELECT * FROM users")
84
+```
85
+
86
+## Class-Based Dependencies
87
+
88
+```python
89
+class Pagination:
90
+ def __init__(self, skip: int = 0, limit: int = 100):
91
+ self.skip = skip
92
+ self.limit = limit
93
+
94
+
95
+@app.get("/items/")
96
+def list_items(request, pagination: Pagination = Depends(Pagination)):
97
+ return {"skip": pagination.skip, "limit": pagination.limit}
98
+```
99
+
100
+## Dependency Scope & Caching
101
+
102
+By default, dependencies are called once per request and the result is cached. All route handlers and nested dependencies share the same instance:
103
+
104
+```python
105
+def get_db(request):
106
+ print("Creating DB session") # Called once per request
107
+ return {"connection": "db-1"}
108
+
109
+def get_user(request, db=Depends(get_db)):
110
+ return {"user": "alice", "db": db}
111
+
112
+@app.get("/profile")
113
+def profile(request, user=Depends(get_user), db=Depends(get_db)):
114
+ return {"user": user, "db_again": db}
115
+```
116
+
117
+In this example, `get_db` is called **once** and its result is reused by `get_user` and `profile`.
118
+
119
+## Dependency with `Path()`, `Query()`, `Header()`
120
+
121
+```python
122
+from justapi import Path, Query, Header
123
+
124
+
125
+def pagination(
126
+ skip: int = Query(0, description="Number of items to skip"),
127
+ limit: int = Query(10, description="Max items per page"),
128
+):
129
+ return {"skip": skip, "limit": limit}
130
+
131
+
132
+def auth(
133
+ user_id: int = Path(..., description="User ID"),
134
+ authorization: str = Header(..., description="Bearer token"),
135
+):
136
+ if authorization != "Bearer secret":
137
+ raise HTTPException(401)
138
+ return {"user_id": user_id}
139
+
140
+
141
+@app.get("/users/{user_id}/items/")
142
+def list_items(
143
+ request,
144
+ auth_data: dict = Depends(auth),
145
+ pagination_data: dict = Depends(pagination),
146
+):
147
+ return {**auth_data, **pagination_data}
148
+```
149
+
150
+## Nested Dependencies
151
+
152
+Dependencies can depend on other dependencies:
153
+
154
+```python
155
+def get_config(request):
156
+ return {"db_url": "postgres://localhost/mydb"}
157
+
158
+
159
+def get_db(request, config: dict = Depends(get_config)):
160
+ return {"connection": f"Connected to {config['db_url']}"}
161
+
162
+
163
+@app.get("/status")
164
+def status(request, db: dict = Depends(get_db)):
165
+ return {"db_status": db["connection"]}
166
+```
167
+
168
+## How It Works Internally
169
+
170
+1. JustAPI inspects function signatures at registration time
171
+2. Parameters with `Depends()` are resolved before the handler runs
172
+3. Dependencies execute in order (innermost first)
173
+4. Results are cached to avoid redundant calls
174
+5. `HTTPException` raised in a dependency short-circuits the request
175
+
176
+## Next Steps
177
+
178
+- [Middleware](/tutorials/middleware/) — Request/response interception
179
+- [Error Handling](/tutorials/error-handling/) — Custom exception handlers
180
+- [Database Integration](/tutorials/database-integration/) — Real database dependencies
0
181