@martinffx/atelier 2.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +94 -253
  3. package/package.json +1 -1
  4. package/skills/{spec-orchestrator → atelier-orchestrator}/SKILL.md +3 -3
  5. package/skills/atelier-setup/SKILL.md +80 -0
  6. package/skills/code-review/references/reviewers.md +3 -3
  7. package/skills/code-review/references/rq.md +6 -6
  8. package/skills/spec-brainstorm/SKILL.md +5 -5
  9. package/skills/spec-implement/SKILL.md +2 -3
  10. package/skills/spec-plan/SKILL.md +21 -29
  11. package/skills/spec-plan/references/plan_template.md +77 -0
  12. package/skills/python-architecture/SKILL.md +0 -148
  13. package/skills/python-architecture/references/data-modeling.md +0 -558
  14. package/skills/python-architecture/references/ddd.md +0 -569
  15. package/skills/python-architecture/references/functional-core.md +0 -337
  16. package/skills/python-build-tools/SKILL.md +0 -153
  17. package/skills/python-build-tools/references/basedpyright.md +0 -321
  18. package/skills/python-build-tools/references/ruff.md +0 -225
  19. package/skills/python-build-tools/references/uv.md +0 -257
  20. package/skills/python-fastapi/SKILL.md +0 -184
  21. package/skills/python-fastapi/references/api-design.md +0 -563
  22. package/skills/python-fastapi/references/dependencies.md +0 -479
  23. package/skills/python-fastapi/references/middleware.md +0 -475
  24. package/skills/python-fastapi/references/validation.md +0 -497
  25. package/skills/python-modern-python/SKILL.md +0 -201
  26. package/skills/python-modern-python/references/async.md +0 -76
  27. package/skills/python-modern-python/references/pattern-matching.md +0 -89
  28. package/skills/python-modern-python/references/typing.md +0 -96
  29. package/skills/python-monorepo/SKILL.md +0 -146
  30. package/skills/python-monorepo/references/docker.md +0 -515
  31. package/skills/python-monorepo/references/namespace-packages.md +0 -426
  32. package/skills/python-monorepo/references/workspace-config.md +0 -341
  33. package/skills/python-sqlalchemy/SKILL.md +0 -119
  34. package/skills/python-sqlalchemy/references/async.md +0 -51
  35. package/skills/python-sqlalchemy/references/models.md +0 -67
  36. package/skills/python-sqlalchemy/references/queries.md +0 -57
  37. package/skills/python-temporal/SKILL.md +0 -128
  38. package/skills/python-temporal/references/patterns.md +0 -87
  39. package/skills/python-temporal/references/testing.md +0 -54
  40. package/skills/python-testing/SKILL.md +0 -161
  41. package/skills/python-testing/references/boundaries.md +0 -449
  42. package/skills/python-testing/references/mocking.md +0 -449
  43. package/skills/python-testing/references/pytest.md +0 -524
  44. package/skills/typescript-api-design/SKILL.md +0 -396
  45. package/skills/typescript-api-design/references/error-responses.md +0 -469
  46. package/skills/typescript-build-tools/SKILL.md +0 -447
  47. package/skills/typescript-build-tools/references/biome.md +0 -436
  48. package/skills/typescript-build-tools/references/bun.md +0 -432
  49. package/skills/typescript-build-tools/references/turborepo.md +0 -426
  50. package/skills/typescript-build-tools/references/vitest.md +0 -518
  51. package/skills/typescript-drizzle-orm/SKILL.md +0 -398
  52. package/skills/typescript-drizzle-orm/references/cloudflare.md +0 -392
  53. package/skills/typescript-drizzle-orm/references/entity-pattern.md +0 -412
  54. package/skills/typescript-drizzle-orm/references/postgresql.md +0 -191
  55. package/skills/typescript-drizzle-orm/references/repository-pattern.md +0 -479
  56. package/skills/typescript-drizzle-orm/references/sqlite.md +0 -291
  57. package/skills/typescript-dynamodb-toolbox/SKILL.md +0 -370
  58. package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +0 -574
  59. package/skills/typescript-dynamodb-toolbox/references/error-handling.md +0 -495
  60. package/skills/typescript-dynamodb-toolbox/references/modeling.md +0 -332
  61. package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +0 -631
  62. package/skills/typescript-dynamodb-toolbox/references/testing.md +0 -718
  63. package/skills/typescript-dynamodb-toolbox/references/transactions.md +0 -423
  64. package/skills/typescript-effect-ts/SKILL.md +0 -341
  65. package/skills/typescript-effect-ts/references/core.md +0 -412
  66. package/skills/typescript-effect-ts/references/error-handling.md +0 -468
  67. package/skills/typescript-effect-ts/references/resources.md +0 -476
  68. package/skills/typescript-effect-ts/references/schema.md +0 -536
  69. package/skills/typescript-effect-ts/references/services.md +0 -461
  70. package/skills/typescript-fastify/SKILL.md +0 -405
  71. package/skills/typescript-fastify/references/plugins.md +0 -329
  72. package/skills/typescript-fastify/references/typeid.md +0 -315
  73. package/skills/typescript-functional-patterns/SKILL.md +0 -402
  74. package/skills/typescript-functional-patterns/references/adts.md +0 -497
  75. package/skills/typescript-functional-patterns/references/branded-types.md +0 -542
  76. package/skills/typescript-functional-patterns/references/migration-guide.md +0 -695
  77. package/skills/typescript-functional-patterns/references/option-result.md +0 -585
  78. package/skills/typescript-testing/SKILL.md +0 -353
  79. package/skills/typescript-testing/references/mocking.md +0 -675
  80. package/skills/typescript-testing/references/msw.md +0 -852
  81. package/skills/typescript-testing/references/snapshot-testing.md +0 -774
@@ -1,475 +0,0 @@
1
- # Middleware in FastAPI
2
-
3
- Middleware runs before and after each request, useful for cross-cutting concerns like logging, timing, CORS, authentication.
4
-
5
- ## Built-in Middleware
6
-
7
- ### CORS Middleware
8
-
9
- ```python
10
- from fastapi.middleware.cors import CORSMiddleware
11
-
12
- app.add_middleware(
13
- CORSMiddleware,
14
- allow_origins=["https://example.com"], # Allowed origins
15
- allow_credentials=True,
16
- allow_methods=["*"], # All HTTP methods
17
- allow_headers=["*"], # All headers
18
- )
19
-
20
- # Development: allow all
21
- app.add_middleware(
22
- CORSMiddleware,
23
- allow_origins=["*"],
24
- allow_credentials=True,
25
- allow_methods=["*"],
26
- allow_headers=["*"],
27
- )
28
- ```
29
-
30
- ### Trusted Host Middleware
31
-
32
- ```python
33
- from fastapi.middleware.trustedhost import TrustedHostMiddleware
34
-
35
- app.add_middleware(
36
- TrustedHostMiddleware,
37
- allowed_hosts=["example.com", "*.example.com"],
38
- )
39
- ```
40
-
41
- ### GZip Middleware
42
-
43
- ```python
44
- from fastapi.middleware.gzip import GZipMiddleware
45
-
46
- app.add_middleware(GZipMiddleware, minimum_size=1000)
47
- ```
48
-
49
- ## Custom Middleware
50
-
51
- ### Function-Based Middleware
52
-
53
- ```python
54
- from fastapi import Request
55
- from starlette.middleware.base import BaseHTTPMiddleware
56
- from time import time
57
-
58
- class TimingMiddleware(BaseHTTPMiddleware):
59
- async def dispatch(self, request: Request, call_next):
60
- start_time = time()
61
-
62
- # Process request
63
- response = await call_next(request)
64
-
65
- # Add timing header
66
- process_time = time() - start_time
67
- response.headers["X-Process-Time"] = str(process_time)
68
-
69
- return response
70
-
71
- app.add_middleware(TimingMiddleware)
72
- ```
73
-
74
- ### Request ID Middleware
75
-
76
- ```python
77
- import uuid
78
- from starlette.middleware.base import BaseHTTPMiddleware
79
-
80
- class RequestIDMiddleware(BaseHTTPMiddleware):
81
- async def dispatch(self, request: Request, call_next):
82
- # Generate request ID
83
- request_id = str(uuid.uuid4())
84
-
85
- # Add to request state
86
- request.state.request_id = request_id
87
-
88
- # Process request
89
- response = await call_next(request)
90
-
91
- # Add to response headers
92
- response.headers["X-Request-ID"] = request_id
93
-
94
- return response
95
-
96
- app.add_middleware(RequestIDMiddleware)
97
-
98
- # Access in route
99
- @app.get("/")
100
- def read_root(request: Request):
101
- request_id = request.state.request_id
102
- return {"request_id": request_id}
103
- ```
104
-
105
- ## Logging Middleware
106
-
107
- ```python
108
- import logging
109
- from time import time
110
-
111
- logger = logging.getLogger(__name__)
112
-
113
- class LoggingMiddleware(BaseHTTPMiddleware):
114
- async def dispatch(self, request: Request, call_next):
115
- # Log request
116
- logger.info(
117
- f"Request: {request.method} {request.url.path}",
118
- extra={
119
- "method": request.method,
120
- "path": request.url.path,
121
- "client": request.client.host,
122
- },
123
- )
124
-
125
- start_time = time()
126
-
127
- # Process request
128
- response = await call_next(request)
129
-
130
- # Log response
131
- process_time = time() - start_time
132
- logger.info(
133
- f"Response: {response.status_code}",
134
- extra={
135
- "status_code": response.status_code,
136
- "process_time": process_time,
137
- },
138
- )
139
-
140
- return response
141
-
142
- app.add_middleware(LoggingMiddleware)
143
- ```
144
-
145
- ## Error Handling Middleware
146
-
147
- ```python
148
- import traceback
149
- from fastapi.responses import JSONResponse
150
-
151
- class ErrorHandlingMiddleware(BaseHTTPMiddleware):
152
- async def dispatch(self, request: Request, call_next):
153
- try:
154
- return await call_next(request)
155
- except Exception as exc:
156
- # Log error
157
- logger.error(
158
- f"Unhandled exception: {exc}",
159
- exc_info=True,
160
- extra={"traceback": traceback.format_exc()},
161
- )
162
-
163
- # Return error response
164
- return JSONResponse(
165
- status_code=500,
166
- content={
167
- "error": "Internal server error",
168
- "detail": str(exc),
169
- },
170
- )
171
-
172
- app.add_middleware(ErrorHandlingMiddleware)
173
- ```
174
-
175
- ## Authentication Middleware
176
-
177
- ```python
178
- from fastapi import HTTPException
179
-
180
- class AuthenticationMiddleware(BaseHTTPMiddleware):
181
- async def dispatch(self, request: Request, call_next):
182
- # Skip auth for public paths
183
- if request.url.path in ["/health", "/docs", "/openapi.json"]:
184
- return await call_next(request)
185
-
186
- # Check auth token
187
- auth_header = request.headers.get("Authorization")
188
- if not auth_header or not auth_header.startswith("Bearer "):
189
- return JSONResponse(
190
- status_code=401,
191
- content={"error": "Missing or invalid authorization header"},
192
- )
193
-
194
- token = auth_header[7:] # Remove "Bearer "
195
-
196
- try:
197
- # Verify token
198
- user = verify_token(token)
199
- request.state.user = user
200
- except Exception:
201
- return JSONResponse(
202
- status_code=401,
203
- content={"error": "Invalid token"},
204
- )
205
-
206
- return await call_next(request)
207
-
208
- app.add_middleware(AuthenticationMiddleware)
209
- ```
210
-
211
- ## Rate Limiting Middleware
212
-
213
- ```python
214
- from collections import defaultdict
215
- from datetime import datetime, timedelta
216
-
217
- class RateLimitMiddleware(BaseHTTPMiddleware):
218
- def __init__(self, app, calls: int = 100, period: int = 60):
219
- super().__init__(app)
220
- self.calls = calls
221
- self.period = period
222
- self.clients = defaultdict(list)
223
-
224
- async def dispatch(self, request: Request, call_next):
225
- client = request.client.host
226
-
227
- # Clean old timestamps
228
- now = datetime.now()
229
- cutoff = now - timedelta(seconds=self.period)
230
- self.clients[client] = [
231
- ts for ts in self.clients[client] if ts > cutoff
232
- ]
233
-
234
- # Check rate limit
235
- if len(self.clients[client]) >= self.calls:
236
- return JSONResponse(
237
- status_code=429,
238
- content={"error": "Rate limit exceeded"},
239
- )
240
-
241
- # Record this request
242
- self.clients[client].append(now)
243
-
244
- return await call_next(request)
245
-
246
- app.add_middleware(RateLimitMiddleware, calls=100, period=60)
247
- ```
248
-
249
- ## Database Session Middleware
250
-
251
- ```python
252
- from sqlalchemy.orm import Session
253
-
254
- class DatabaseSessionMiddleware(BaseHTTPMiddleware):
255
- async def dispatch(self, request: Request, call_next):
256
- # Create session
257
- db = SessionLocal()
258
- request.state.db = db
259
-
260
- try:
261
- response = await call_next(request)
262
- # Commit on success
263
- db.commit()
264
- return response
265
- except Exception:
266
- # Rollback on error
267
- db.rollback()
268
- raise
269
- finally:
270
- db.close()
271
-
272
- app.add_middleware(DatabaseSessionMiddleware)
273
-
274
- # Access in route
275
- @app.get("/users")
276
- def list_users(request: Request):
277
- db: Session = request.state.db
278
- return db.query(User).all()
279
- ```
280
-
281
- ## Middleware Order
282
-
283
- Middleware runs in reverse order of addition:
284
-
285
- ```python
286
- # Execution order: Error → Logging → Timing → CORS → Route
287
-
288
- app.add_middleware(CORSMiddleware, ...) # 4. CORS (innermost)
289
- app.add_middleware(TimingMiddleware) # 3. Timing
290
- app.add_middleware(LoggingMiddleware) # 2. Logging
291
- app.add_middleware(ErrorHandlingMiddleware) # 1. Error (outermost)
292
- ```
293
-
294
- **Request flow:**
295
- 1. ErrorHandlingMiddleware (enter)
296
- 2. LoggingMiddleware (enter)
297
- 3. TimingMiddleware (enter)
298
- 4. CORSMiddleware (enter)
299
- 5. Route handler
300
- 6. CORSMiddleware (exit)
301
- 7. TimingMiddleware (exit)
302
- 8. LoggingMiddleware (exit)
303
- 9. ErrorHandlingMiddleware (exit)
304
-
305
- ## Pure ASGI Middleware
306
-
307
- For maximum performance, use pure ASGI middleware:
308
-
309
- ```python
310
- from starlette.types import ASGIApp, Receive, Scope, Send
311
-
312
- class SimpleMiddleware:
313
- def __init__(self, app: ASGIApp):
314
- self.app = app
315
-
316
- async def __call__(self, scope: Scope, receive: Receive, send: Send):
317
- if scope["type"] != "http":
318
- await self.app(scope, receive, send)
319
- return
320
-
321
- # Wrap send to modify response
322
- async def send_wrapper(message):
323
- if message["type"] == "http.response.start":
324
- headers = list(message["headers"])
325
- headers.append((b"x-custom-header", b"value"))
326
- message["headers"] = headers
327
- await send(message)
328
-
329
- await self.app(scope, receive, send_wrapper)
330
-
331
- app.add_middleware(SimpleMiddleware)
332
- ```
333
-
334
- ## Request State
335
-
336
- Share data between middleware and routes:
337
-
338
- ```python
339
- class ContextMiddleware(BaseHTTPMiddleware):
340
- async def dispatch(self, request: Request, call_next):
341
- # Set request state
342
- request.state.request_id = str(uuid.uuid4())
343
- request.state.start_time = time()
344
-
345
- response = await call_next(request)
346
-
347
- # Access request state
348
- duration = time() - request.state.start_time
349
- response.headers["X-Duration"] = str(duration)
350
-
351
- return response
352
-
353
- @app.get("/")
354
- def read_root(request: Request):
355
- # Access state set by middleware
356
- request_id = request.state.request_id
357
- return {"request_id": request_id}
358
- ```
359
-
360
- ## Conditional Middleware
361
-
362
- Apply middleware conditionally:
363
-
364
- ```python
365
- class ConditionalMiddleware(BaseHTTPMiddleware):
366
- async def dispatch(self, request: Request, call_next):
367
- # Skip for specific paths
368
- if request.url.path.startswith("/static"):
369
- return await call_next(request)
370
-
371
- # Skip for specific methods
372
- if request.method == "OPTIONS":
373
- return await call_next(request)
374
-
375
- # Apply middleware logic
376
- # ...
377
-
378
- return await call_next(request)
379
- ```
380
-
381
- ## Response Modification
382
-
383
- Modify response before returning:
384
-
385
- ```python
386
- class ResponseModificationMiddleware(BaseHTTPMiddleware):
387
- async def dispatch(self, request: Request, call_next):
388
- response = await call_next(request)
389
-
390
- # Add security headers
391
- response.headers["X-Content-Type-Options"] = "nosniff"
392
- response.headers["X-Frame-Options"] = "DENY"
393
- response.headers["X-XSS-Protection"] = "1; mode=block"
394
-
395
- return response
396
-
397
- app.add_middleware(ResponseModificationMiddleware)
398
- ```
399
-
400
- ## Testing Middleware
401
-
402
- ```python
403
- from fastapi.testclient import TestClient
404
-
405
- def test_timing_middleware():
406
- client = TestClient(app)
407
- response = client.get("/")
408
-
409
- assert "X-Process-Time" in response.headers
410
- assert float(response.headers["X-Process-Time"]) > 0
411
-
412
- def test_request_id_middleware():
413
- client = TestClient(app)
414
- response = client.get("/")
415
-
416
- assert "X-Request-ID" in response.headers
417
- assert len(response.headers["X-Request-ID"]) == 36 # UUID length
418
- ```
419
-
420
- ## Best Practices
421
-
422
- 1. **Order matters**: Add middleware in correct order (error handling first)
423
- 2. **Use request.state**: Share data between middleware and routes
424
- 3. **Handle errors**: Catch exceptions in middleware
425
- 4. **Performance**: Keep middleware fast (runs on every request)
426
- 5. **Skip when needed**: Don't apply to health checks, static files
427
- 6. **Type hints**: Use proper types for Request, Response
428
- 7. **Pure ASGI for performance**: Use ASGI middleware for hot paths
429
-
430
- ## Common Middleware Stack
431
-
432
- Recommended middleware order:
433
-
434
- ```python
435
- # 1. Error handling (outermost)
436
- app.add_middleware(ErrorHandlingMiddleware)
437
-
438
- # 2. Logging
439
- app.add_middleware(LoggingMiddleware)
440
-
441
- # 3. Timing
442
- app.add_middleware(TimingMiddleware)
443
-
444
- # 4. Rate limiting
445
- app.add_middleware(RateLimitMiddleware)
446
-
447
- # 5. Authentication
448
- app.add_middleware(AuthenticationMiddleware)
449
-
450
- # 6. Database session
451
- app.add_middleware(DatabaseSessionMiddleware)
452
-
453
- # 7. CORS (innermost)
454
- app.add_middleware(CORSMiddleware, ...)
455
- ```
456
-
457
- ## Summary
458
-
459
- **Built-in middleware:**
460
- - CORSMiddleware (cross-origin requests)
461
- - TrustedHostMiddleware (host validation)
462
- - GZipMiddleware (compression)
463
-
464
- **Custom middleware:**
465
- - Timing (performance monitoring)
466
- - Logging (request/response logging)
467
- - Error handling (catch all exceptions)
468
- - Authentication (verify tokens)
469
- - Rate limiting (prevent abuse)
470
-
471
- **Key concepts:**
472
- - Middleware runs on every request
473
- - Order matters (reverse order of addition)
474
- - Use `request.state` for sharing data
475
- - Keep middleware fast