@malamute/ai-rules 1.0.0 → 1.3.0

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 (145) hide show
  1. package/README.md +272 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/CLAUDE.md +52 -149
  4. package/configs/_shared/rules/conventions/documentation.md +324 -0
  5. package/configs/_shared/rules/conventions/git.md +265 -0
  6. package/configs/_shared/rules/conventions/npm.md +80 -0
  7. package/configs/_shared/{.claude/rules → rules/conventions}/performance.md +1 -1
  8. package/configs/_shared/rules/conventions/principles.md +334 -0
  9. package/configs/_shared/rules/devops/ci-cd.md +262 -0
  10. package/configs/_shared/rules/devops/docker.md +275 -0
  11. package/configs/_shared/rules/devops/nx.md +194 -0
  12. package/configs/_shared/rules/domain/backend/api-design.md +203 -0
  13. package/configs/_shared/rules/lang/csharp/async.md +220 -0
  14. package/configs/_shared/rules/lang/csharp/csharp.md +314 -0
  15. package/configs/_shared/rules/lang/csharp/linq.md +210 -0
  16. package/configs/_shared/rules/lang/python/async.md +337 -0
  17. package/configs/_shared/rules/lang/python/celery.md +476 -0
  18. package/configs/_shared/rules/lang/python/config.md +339 -0
  19. package/configs/{python/.claude/rules → _shared/rules/lang/python}/database/sqlalchemy.md +6 -1
  20. package/configs/_shared/rules/lang/python/deployment.md +523 -0
  21. package/configs/_shared/rules/lang/python/error-handling.md +330 -0
  22. package/configs/_shared/rules/lang/python/migrations.md +421 -0
  23. package/configs/_shared/rules/lang/python/python.md +172 -0
  24. package/configs/_shared/rules/lang/python/repository.md +383 -0
  25. package/configs/{python/.claude/rules → _shared/rules/lang/python}/testing.md +2 -69
  26. package/configs/_shared/rules/lang/typescript/async.md +447 -0
  27. package/configs/_shared/rules/lang/typescript/generics.md +356 -0
  28. package/configs/_shared/rules/lang/typescript/typescript.md +212 -0
  29. package/configs/_shared/rules/quality/error-handling.md +48 -0
  30. package/configs/_shared/rules/quality/logging.md +45 -0
  31. package/configs/_shared/rules/quality/observability.md +240 -0
  32. package/configs/_shared/rules/quality/testing-patterns.md +65 -0
  33. package/configs/_shared/rules/security/secrets-management.md +222 -0
  34. package/configs/_shared/skills/analysis/explore/SKILL.md +257 -0
  35. package/configs/_shared/skills/analysis/security-audit/SKILL.md +184 -0
  36. package/configs/_shared/skills/dev/api-endpoint/SKILL.md +126 -0
  37. package/configs/_shared/{.claude/commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  38. package/configs/_shared/{.claude/commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  39. package/configs/_shared/{.claude/commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  40. package/configs/_shared/skills/infra/deploy/SKILL.md +139 -0
  41. package/configs/_shared/skills/infra/docker/SKILL.md +95 -0
  42. package/configs/_shared/skills/infra/migration/SKILL.md +158 -0
  43. package/configs/_shared/skills/nx/nx-affected/SKILL.md +72 -0
  44. package/configs/_shared/skills/nx/nx-lib/SKILL.md +375 -0
  45. package/configs/angular/CLAUDE.md +24 -216
  46. package/configs/angular/{.claude/rules → rules/core}/components.md +69 -15
  47. package/configs/angular/rules/core/resource.md +285 -0
  48. package/configs/angular/rules/core/signals.md +323 -0
  49. package/configs/angular/rules/http.md +338 -0
  50. package/configs/angular/rules/routing.md +291 -0
  51. package/configs/angular/rules/ssr.md +312 -0
  52. package/configs/angular/rules/state/signal-store.md +408 -0
  53. package/configs/angular/{.claude/rules → rules/state}/state.md +2 -2
  54. package/configs/angular/{.claude/rules → rules}/testing.md +7 -7
  55. package/configs/angular/rules/ui/aria.md +422 -0
  56. package/configs/angular/rules/ui/forms.md +424 -0
  57. package/configs/angular/rules/ui/pipes-directives.md +335 -0
  58. package/configs/angular/{.claude/settings.json → settings.json} +3 -0
  59. package/configs/dotnet/CLAUDE.md +53 -286
  60. package/configs/dotnet/rules/background-services.md +552 -0
  61. package/configs/dotnet/rules/configuration.md +426 -0
  62. package/configs/dotnet/rules/ddd.md +447 -0
  63. package/configs/dotnet/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/rules/mediatr.md +320 -0
  65. package/configs/dotnet/rules/middleware.md +489 -0
  66. package/configs/dotnet/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/rules/validation.md +388 -0
  68. package/configs/dotnet/settings.json +29 -0
  69. package/configs/fastapi/CLAUDE.md +144 -0
  70. package/configs/fastapi/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/rules/dependencies.md +170 -0
  72. package/configs/{python/.claude → fastapi}/rules/fastapi.md +61 -1
  73. package/configs/fastapi/rules/lifespan.md +274 -0
  74. package/configs/fastapi/rules/middleware.md +229 -0
  75. package/configs/fastapi/rules/pydantic.md +433 -0
  76. package/configs/fastapi/rules/responses.md +251 -0
  77. package/configs/fastapi/rules/routers.md +202 -0
  78. package/configs/fastapi/rules/security.md +222 -0
  79. package/configs/fastapi/rules/testing.md +251 -0
  80. package/configs/fastapi/rules/websockets.md +298 -0
  81. package/configs/fastapi/settings.json +35 -0
  82. package/configs/flask/CLAUDE.md +166 -0
  83. package/configs/flask/rules/blueprints.md +208 -0
  84. package/configs/flask/rules/cli.md +285 -0
  85. package/configs/flask/rules/configuration.md +281 -0
  86. package/configs/flask/rules/context.md +238 -0
  87. package/configs/flask/rules/error-handlers.md +278 -0
  88. package/configs/flask/rules/extensions.md +278 -0
  89. package/configs/flask/rules/flask.md +171 -0
  90. package/configs/flask/rules/marshmallow.md +206 -0
  91. package/configs/flask/rules/security.md +267 -0
  92. package/configs/flask/rules/testing.md +284 -0
  93. package/configs/flask/settings.json +35 -0
  94. package/configs/nestjs/CLAUDE.md +57 -215
  95. package/configs/nestjs/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/rules/filters.md +376 -0
  97. package/configs/nestjs/rules/interceptors.md +317 -0
  98. package/configs/nestjs/rules/middleware.md +321 -0
  99. package/configs/nestjs/{.claude/rules → rules}/modules.md +26 -0
  100. package/configs/nestjs/rules/pipes.md +351 -0
  101. package/configs/nestjs/rules/websockets.md +451 -0
  102. package/configs/nestjs/settings.json +31 -0
  103. package/configs/nextjs/CLAUDE.md +69 -331
  104. package/configs/nextjs/rules/api-routes.md +358 -0
  105. package/configs/nextjs/rules/authentication.md +355 -0
  106. package/configs/nextjs/{.claude/rules → rules}/components.md +52 -0
  107. package/configs/nextjs/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/rules/database.md +400 -0
  109. package/configs/nextjs/rules/middleware.md +303 -0
  110. package/configs/nextjs/rules/routing.md +324 -0
  111. package/configs/nextjs/rules/seo.md +350 -0
  112. package/configs/nextjs/rules/server-actions.md +353 -0
  113. package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +6 -6
  114. package/configs/nextjs/{.claude/settings.json → settings.json} +7 -0
  115. package/package.json +24 -9
  116. package/src/cli.js +218 -0
  117. package/src/config.js +63 -0
  118. package/src/index.js +4 -0
  119. package/src/installer.js +414 -0
  120. package/src/merge.js +109 -0
  121. package/src/tech-config.json +45 -0
  122. package/src/utils.js +88 -0
  123. package/configs/dotnet/.claude/settings.json +0 -9
  124. package/configs/nestjs/.claude/settings.json +0 -15
  125. package/configs/python/.claude/rules/flask.md +0 -332
  126. package/configs/python/.claude/settings.json +0 -18
  127. package/configs/python/CLAUDE.md +0 -273
  128. package/src/install.js +0 -315
  129. /package/configs/_shared/{.claude/rules → rules/domain/frontend}/accessibility.md +0 -0
  130. /package/configs/_shared/{.claude/rules → rules/security}/security.md +0 -0
  131. /package/configs/_shared/{.claude/skills → skills/dev}/debug/SKILL.md +0 -0
  132. /package/configs/_shared/{.claude/skills → skills/dev}/learning/SKILL.md +0 -0
  133. /package/configs/_shared/{.claude/skills → skills/dev}/spec/SKILL.md +0 -0
  134. /package/configs/_shared/{.claude/skills → skills/git}/review/SKILL.md +0 -0
  135. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  136. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  137. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  138. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  139. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  140. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  141. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  142. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  143. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  144. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  145. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -0,0 +1,274 @@
1
+ ---
2
+ paths:
3
+ - "**/*.py"
4
+ ---
5
+
6
+ # FastAPI Lifespan Management
7
+
8
+ ## Lifespan Context Manager
9
+
10
+ ```python
11
+ from contextlib import asynccontextmanager
12
+ from fastapi import FastAPI
13
+
14
+ @asynccontextmanager
15
+ async def lifespan(app: FastAPI):
16
+ # Startup: runs before app starts accepting requests
17
+ print("Starting up...")
18
+ app.state.db = await create_database_pool()
19
+ app.state.redis = await create_redis_pool()
20
+
21
+ yield # App runs here
22
+
23
+ # Shutdown: runs when app is shutting down
24
+ print("Shutting down...")
25
+ await app.state.db.close()
26
+ await app.state.redis.close()
27
+
28
+ app = FastAPI(lifespan=lifespan)
29
+ ```
30
+
31
+ ## Database Connection Pool
32
+
33
+ ```python
34
+ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
35
+
36
+ @asynccontextmanager
37
+ async def lifespan(app: FastAPI):
38
+ # Create engine
39
+ engine = create_async_engine(
40
+ settings.database_url,
41
+ pool_size=10,
42
+ max_overflow=20,
43
+ pool_pre_ping=True,
44
+ )
45
+
46
+ # Create session factory
47
+ app.state.async_session = async_sessionmaker(
48
+ engine,
49
+ expire_on_commit=False,
50
+ )
51
+
52
+ yield
53
+
54
+ # Dispose engine
55
+ await engine.dispose()
56
+
57
+ # Dependency
58
+ async def get_db() -> AsyncGenerator[AsyncSession, None]:
59
+ async with app.state.async_session() as session:
60
+ yield session
61
+ ```
62
+
63
+ ## Redis Connection
64
+
65
+ ```python
66
+ import redis.asyncio as redis
67
+
68
+ @asynccontextmanager
69
+ async def lifespan(app: FastAPI):
70
+ # Redis pool
71
+ app.state.redis = await redis.from_url(
72
+ settings.redis_url,
73
+ encoding="utf-8",
74
+ decode_responses=True,
75
+ )
76
+
77
+ yield
78
+
79
+ await app.state.redis.close()
80
+
81
+ # Usage
82
+ @router.get("/cached/{key}")
83
+ async def get_cached(key: str, request: Request) -> dict:
84
+ value = await request.app.state.redis.get(key)
85
+ return {"key": key, "value": value}
86
+ ```
87
+
88
+ ## HTTP Client Pool
89
+
90
+ ```python
91
+ import httpx
92
+
93
+ @asynccontextmanager
94
+ async def lifespan(app: FastAPI):
95
+ # Create shared HTTP client
96
+ app.state.http_client = httpx.AsyncClient(
97
+ timeout=30.0,
98
+ limits=httpx.Limits(max_connections=100),
99
+ )
100
+
101
+ yield
102
+
103
+ await app.state.http_client.aclose()
104
+
105
+ # Usage
106
+ async def call_external_api(request: Request) -> dict:
107
+ client = request.app.state.http_client
108
+ response = await client.get("https://api.example.com/data")
109
+ return response.json()
110
+ ```
111
+
112
+ ## Background Task Scheduler
113
+
114
+ ```python
115
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
116
+
117
+ @asynccontextmanager
118
+ async def lifespan(app: FastAPI):
119
+ scheduler = AsyncIOScheduler()
120
+
121
+ # Add scheduled jobs
122
+ scheduler.add_job(
123
+ cleanup_expired_sessions,
124
+ "interval",
125
+ hours=1,
126
+ id="cleanup_sessions",
127
+ )
128
+
129
+ scheduler.add_job(
130
+ send_daily_reports,
131
+ "cron",
132
+ hour=9,
133
+ minute=0,
134
+ id="daily_reports",
135
+ )
136
+
137
+ scheduler.start()
138
+ app.state.scheduler = scheduler
139
+
140
+ yield
141
+
142
+ scheduler.shutdown(wait=True)
143
+ ```
144
+
145
+ ## Multiple Resources
146
+
147
+ ```python
148
+ @asynccontextmanager
149
+ async def lifespan(app: FastAPI):
150
+ # Initialize all resources
151
+ engine = create_async_engine(settings.database_url)
152
+ redis_pool = await redis.from_url(settings.redis_url)
153
+ http_client = httpx.AsyncClient()
154
+
155
+ # Store in app state
156
+ app.state.db_engine = engine
157
+ app.state.async_session = async_sessionmaker(engine)
158
+ app.state.redis = redis_pool
159
+ app.state.http_client = http_client
160
+
161
+ # Run migrations
162
+ async with engine.begin() as conn:
163
+ await conn.run_sync(Base.metadata.create_all)
164
+
165
+ # Log startup
166
+ logger.info("Application started", extra={
167
+ "database": settings.database_url,
168
+ "redis": settings.redis_url,
169
+ })
170
+
171
+ yield
172
+
173
+ # Cleanup in reverse order
174
+ await http_client.aclose()
175
+ await redis_pool.close()
176
+ await engine.dispose()
177
+
178
+ logger.info("Application shutdown complete")
179
+ ```
180
+
181
+ ## Health Checks with Lifespan
182
+
183
+ ```python
184
+ from dataclasses import dataclass, field
185
+ from datetime import datetime
186
+
187
+ @dataclass
188
+ class AppHealth:
189
+ started_at: datetime = field(default_factory=datetime.utcnow)
190
+ ready: bool = False
191
+ checks: dict = field(default_factory=dict)
192
+
193
+ @asynccontextmanager
194
+ async def lifespan(app: FastAPI):
195
+ health = AppHealth()
196
+ app.state.health = health
197
+
198
+ # Initialize resources
199
+ try:
200
+ app.state.db = await create_database_pool()
201
+ health.checks["database"] = "ok"
202
+ except Exception as e:
203
+ health.checks["database"] = f"error: {e}"
204
+
205
+ try:
206
+ app.state.redis = await create_redis_pool()
207
+ health.checks["redis"] = "ok"
208
+ except Exception as e:
209
+ health.checks["redis"] = f"error: {e}"
210
+
211
+ health.ready = all(v == "ok" for v in health.checks.values())
212
+
213
+ yield
214
+
215
+ health.ready = False
216
+ await cleanup_resources(app)
217
+
218
+ @router.get("/health/live")
219
+ async def liveness() -> dict:
220
+ return {"status": "ok"}
221
+
222
+ @router.get("/health/ready")
223
+ async def readiness(request: Request) -> dict:
224
+ health = request.app.state.health
225
+
226
+ if not health.ready:
227
+ raise HTTPException(503, detail=health.checks)
228
+
229
+ return {
230
+ "status": "ready",
231
+ "uptime": (datetime.utcnow() - health.started_at).total_seconds(),
232
+ "checks": health.checks,
233
+ }
234
+ ```
235
+
236
+ ## Deprecated: on_event
237
+
238
+ ```python
239
+ # DEPRECATED - Don't use this pattern
240
+ @app.on_event("startup")
241
+ async def startup():
242
+ ...
243
+
244
+ @app.on_event("shutdown")
245
+ async def shutdown():
246
+ ...
247
+
248
+ # Use lifespan instead (see above)
249
+ ```
250
+
251
+ ## Testing with Lifespan
252
+
253
+ ```python
254
+ import pytest
255
+ from httpx import AsyncClient, ASGITransport
256
+
257
+ @pytest.fixture
258
+ async def client():
259
+ # Lifespan is automatically handled
260
+ async with AsyncClient(
261
+ transport=ASGITransport(app=app),
262
+ base_url="http://test",
263
+ ) as client:
264
+ yield client
265
+
266
+ # Or override lifespan for tests
267
+ @asynccontextmanager
268
+ async def test_lifespan(app: FastAPI):
269
+ app.state.db = MockDatabase()
270
+ app.state.redis = MockRedis()
271
+ yield
272
+
273
+ app_for_testing = FastAPI(lifespan=test_lifespan)
274
+ ```
@@ -0,0 +1,229 @@
1
+ ---
2
+ paths:
3
+ - "**/*.py"
4
+ ---
5
+
6
+ # FastAPI Middleware Patterns
7
+
8
+ ## Custom Middleware
9
+
10
+ ```python
11
+ from starlette.middleware.base import BaseHTTPMiddleware
12
+ from starlette.requests import Request
13
+ from starlette.responses import Response
14
+ import time
15
+
16
+ class TimingMiddleware(BaseHTTPMiddleware):
17
+ async def dispatch(self, request: Request, call_next) -> Response:
18
+ start_time = time.perf_counter()
19
+
20
+ response = await call_next(request)
21
+
22
+ process_time = time.perf_counter() - start_time
23
+ response.headers["X-Process-Time"] = f"{process_time:.4f}"
24
+
25
+ return response
26
+
27
+ app.add_middleware(TimingMiddleware)
28
+ ```
29
+
30
+ ## Request ID Middleware
31
+
32
+ ```python
33
+ import uuid
34
+ from contextvars import ContextVar
35
+
36
+ request_id_ctx: ContextVar[str] = ContextVar("request_id", default="")
37
+
38
+ class RequestIDMiddleware(BaseHTTPMiddleware):
39
+ async def dispatch(self, request: Request, call_next) -> Response:
40
+ request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
41
+ request_id_ctx.set(request_id)
42
+
43
+ response = await call_next(request)
44
+ response.headers["X-Request-ID"] = request_id
45
+
46
+ return response
47
+
48
+ def get_request_id() -> str:
49
+ return request_id_ctx.get()
50
+ ```
51
+
52
+ ## Logging Middleware
53
+
54
+ ```python
55
+ import logging
56
+ from typing import Callable
57
+
58
+ logger = logging.getLogger(__name__)
59
+
60
+ class LoggingMiddleware(BaseHTTPMiddleware):
61
+ async def dispatch(self, request: Request, call_next) -> Response:
62
+ # Log request
63
+ logger.info(
64
+ "Request started",
65
+ extra={
66
+ "method": request.method,
67
+ "path": request.url.path,
68
+ "client_ip": request.client.host,
69
+ "request_id": get_request_id(),
70
+ },
71
+ )
72
+
73
+ start_time = time.perf_counter()
74
+
75
+ try:
76
+ response = await call_next(request)
77
+ except Exception as e:
78
+ logger.exception(
79
+ "Request failed",
80
+ extra={
81
+ "path": request.url.path,
82
+ "error": str(e),
83
+ "request_id": get_request_id(),
84
+ },
85
+ )
86
+ raise
87
+
88
+ duration = time.perf_counter() - start_time
89
+
90
+ # Log response
91
+ logger.info(
92
+ "Request completed",
93
+ extra={
94
+ "method": request.method,
95
+ "path": request.url.path,
96
+ "status_code": response.status_code,
97
+ "duration": f"{duration:.4f}s",
98
+ "request_id": get_request_id(),
99
+ },
100
+ )
101
+
102
+ return response
103
+ ```
104
+
105
+ ## Error Handling Middleware
106
+
107
+ ```python
108
+ from fastapi.responses import JSONResponse
109
+
110
+ class ErrorHandlingMiddleware(BaseHTTPMiddleware):
111
+ async def dispatch(self, request: Request, call_next) -> Response:
112
+ try:
113
+ return await call_next(request)
114
+ except ValueError as e:
115
+ return JSONResponse(
116
+ status_code=400,
117
+ content={"error": "Bad Request", "detail": str(e)},
118
+ )
119
+ except PermissionError as e:
120
+ return JSONResponse(
121
+ status_code=403,
122
+ content={"error": "Forbidden", "detail": str(e)},
123
+ )
124
+ except Exception as e:
125
+ logger.exception("Unhandled exception")
126
+ return JSONResponse(
127
+ status_code=500,
128
+ content={"error": "Internal Server Error"},
129
+ )
130
+ ```
131
+
132
+ ## Pure ASGI Middleware
133
+
134
+ ```python
135
+ # More performant than BaseHTTPMiddleware
136
+ class PureASGIMiddleware:
137
+ def __init__(self, app):
138
+ self.app = app
139
+
140
+ async def __call__(self, scope, receive, send):
141
+ if scope["type"] != "http":
142
+ await self.app(scope, receive, send)
143
+ return
144
+
145
+ # Before request
146
+ start_time = time.perf_counter()
147
+
148
+ async def send_wrapper(message):
149
+ if message["type"] == "http.response.start":
150
+ # Add headers
151
+ headers = list(message.get("headers", []))
152
+ duration = time.perf_counter() - start_time
153
+ headers.append((b"x-process-time", f"{duration:.4f}".encode()))
154
+ message["headers"] = headers
155
+ await send(message)
156
+
157
+ await self.app(scope, receive, send_wrapper)
158
+
159
+ app.add_middleware(PureASGIMiddleware)
160
+ ```
161
+
162
+ ## Database Session Middleware
163
+
164
+ ```python
165
+ from contextvars import ContextVar
166
+
167
+ db_session_ctx: ContextVar[AsyncSession | None] = ContextVar("db_session", default=None)
168
+
169
+ class DatabaseSessionMiddleware(BaseHTTPMiddleware):
170
+ async def dispatch(self, request: Request, call_next) -> Response:
171
+ async with async_session() as session:
172
+ db_session_ctx.set(session)
173
+ try:
174
+ response = await call_next(request)
175
+ await session.commit()
176
+ except Exception:
177
+ await session.rollback()
178
+ raise
179
+ finally:
180
+ db_session_ctx.set(None)
181
+
182
+ return response
183
+
184
+ def get_db() -> AsyncSession:
185
+ session = db_session_ctx.get()
186
+ if session is None:
187
+ raise RuntimeError("No database session available")
188
+ return session
189
+ ```
190
+
191
+ ## Middleware Order
192
+
193
+ ```python
194
+ # Order matters! Last added = first executed
195
+ # Request flow: A -> B -> C -> Handler -> C -> B -> A
196
+
197
+ app.add_middleware(ErrorHandlingMiddleware) # 3rd (innermost)
198
+ app.add_middleware(LoggingMiddleware) # 2nd
199
+ app.add_middleware(RequestIDMiddleware) # 1st (outermost)
200
+ ```
201
+
202
+ ## Built-in Middleware
203
+
204
+ ```python
205
+ from fastapi.middleware.cors import CORSMiddleware
206
+ from fastapi.middleware.trustedhost import TrustedHostMiddleware
207
+ from fastapi.middleware.gzip import GZipMiddleware
208
+ from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
209
+
210
+ # HTTPS redirect
211
+ app.add_middleware(HTTPSRedirectMiddleware)
212
+
213
+ # Trusted hosts
214
+ app.add_middleware(
215
+ TrustedHostMiddleware,
216
+ allowed_hosts=["example.com", "*.example.com"],
217
+ )
218
+
219
+ # GZip compression
220
+ app.add_middleware(GZipMiddleware, minimum_size=1000)
221
+
222
+ # CORS
223
+ app.add_middleware(
224
+ CORSMiddleware,
225
+ allow_origins=["https://example.com"],
226
+ allow_methods=["*"],
227
+ allow_headers=["*"],
228
+ )
229
+ ```