p2e: add Middleware tutorial — basic, CORS, secure headers, request logging, rate limiter pattern

swadhinbiswas committed 3 months ago
9776b10
1 parent 3bb94a5
commit 9776b10
1 file changed
+176 -0
▼docs_site/src/content/docs/tutorials/middleware.md
+176-0
1
+---
2
+title: Middleware
3
+description: Intercept requests and responses with middleware for logging, timing, CORS, and custom processing.
4
+---
5
+
6
+Middleware lets you run code before and after every request. JustAPI supports the same middleware pattern as FastAPI and Starlette.
7
+
8
+## Basic HTTP Middleware
9
+
10
+Use the `@app.middleware("http")` decorator:
11
+
12
+```python
13
+import time
14
+from justapi import JustAPIApp
15
+
16
+app = JustAPIApp()
17
+
18
+
19
+@app.middleware("http")
20
+def timing_middleware(request, call_next):
21
+ start = time.time()
22
+ response = call_next(request)
23
+ elapsed = time.time() - start
24
+ response["headers"] = response.get("headers", []) + [
25
+ (b"X-Process-Time", str(elapsed).encode())
26
+ ]
27
+ return response
28
+```
29
+
30
+The middleware receives the `request` and a `call_next` function that dispatches the request down the chain. The return value is a response dict.
31
+
32
+## CORS Middleware
33
+
34
+JustAPI has a built-in CORS configuration method:
35
+
36
+```python
37
+app.add_cors(
38
+ allow_origins=["https://example.com", "https://api.example.com"],
39
+ allow_methods=["GET", "POST", "PUT", "DELETE"],
40
+ allow_headers=["*"],
41
+ allow_credentials=True,
42
+)
43
+```
44
+
45
+| Parameter | Type | Default | Description |
46
+|---|---|---|---|
47
+| `allow_origins` | list[str] | `["*"]` | Allowed origins |
48
+| `allow_methods` | list[str] | All methods | Allowed HTTP methods |
49
+| `allow_headers` | list[str] | `["*"]` | Allowed headers |
50
+| `allow_credentials` | bool | `False` | Allow cookies/auth headers |
51
+
52
+## Secure Headers Middleware
53
+
54
+```python
55
+app.enable_secure_headers(with_hsts=True)
56
+```
57
+
58
+This adds:
59
+- `X-Content-Type-Options: nosniff`
60
+- `X-Frame-Options: DENY`
61
+- `Strict-Transport-Security` (when `with_hsts=True`)
62
+- `Content-Security-Policy`
63
+- `X-XSS-Protection: 0`
64
+- `Referrer-Policy`
65
+
66
+## Multiple Middleware
67
+
68
+Middlewares are executed in registration order:
69
+
70
+```python
71
+@app.middleware("http")
72
+def middleware_one(request, call_next):
73
+ print("Before 1")
74
+ response = call_next(request)
75
+ print("After 1")
76
+ return response
77
+
78
+
79
+@app.middleware("http")
80
+def middleware_two(request, call_next):
81
+ print("Before 2")
82
+ response = call_next(request)
83
+ print("After 2")
84
+ return response
85
+```
86
+
87
+Output for a single request:
88
+
89
+```
90
+Before 1
91
+Before 2
92
+[handler executes]
93
+After 2
94
+After 1
95
+```
96
+
97
+## Route-Level Middleware
98
+
99
+You can add middleware to specific routes or routers using dependencies:
100
+
101
+```python
102
+from justapi import Depends, HTTPException, Header
103
+
104
+
105
+def require_admin(authorization: str = Header(...)):
106
+ if authorization != "Bearer admin-token":
107
+ raise HTTPException(403, "Admin access required")
108
+
109
+
110
+@router.get("/admin/users", dependencies=[Depends(require_admin)])
111
+def list_all_users(request):
112
+ return {"users": [...]}
113
+
114
+
115
+app.include_router(router, prefix="/api/v1")
116
+```
117
+
118
+## Real-World Patterns
119
+
120
+### Request Logging Middleware
121
+
122
+```python
123
+import logging
124
+
125
+logger = logging.getLogger("justapi")
126
+
127
+
128
+@app.middleware("http")
129
+def log_requests(request, call_next):
130
+ method = request.method
131
+ path = request.path
132
+ logger.info(f"→ {method} {path}")
133
+ response = call_next(request)
134
+ status = response.get("status", 200)
135
+ logger.info(f"← {method} {path} → {status}")
136
+ return response
137
+```
138
+
139
+### Rate Limiting Middleware (via Dependency)
140
+
141
+```python
142
+from justapi import Depends, HTTPException, Header
143
+import time
144
+
145
+request_counts = {}
146
+
147
+def rate_limiter(ip: str = Header("X-Forwarded-For")):
148
+ now = time.time()
149
+ window = 60 # 1 minute
150
+ max_requests = 100
151
+
152
+ # Clean old entries
153
+ request_counts[ip] = [t for t in request_counts.get(ip, []) if now - t < window]
154
+ request_counts[ip].append(now)
155
+
156
+ if len(request_counts[ip]) > max_requests:
157
+ raise HTTPException(429, "Rate limit exceeded")
158
+
159
+
160
+@app.get("/api/", dependencies=[Depends(rate_limiter)])
161
+def api_endpoint(request):
162
+ return {"data": "ok"}
163
+```
164
+
165
+## How It Works Internally
166
+
167
+1. Middleware runs in the Rust middleware chain, not Python
168
+2. `call_next` dispatches through the chain to the handler
169
+3. The response dict flows back through each middleware's post-processing
170
+4. Built-in middleware (CORS, rate limiting) runs in Rust with zero GIL overhead
171
+
172
+## Next Steps
173
+
174
+- [Error Handling](/tutorials/error-handling/) — Custom error responses
175
+- [Dependency Injection](/tutorials/dependency-injection/) — Reusable components
176
+- [Resilience Patterns](/advanced/resilience-patterns/) — Circuit breakers, rate limiting
0
177