claude-mpm 4.17.0__py3-none-any.whl → 4.18.0__py3-none-any.whl
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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/agent_loader.py +4 -4
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/mcp_config_manager.py +2 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/unified/deployment_strategies/local.py +1 -1
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/METADATA +68 -1
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/RECORD +47 -24
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1199 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill_id: fastapi-local-dev
|
|
3
|
+
skill_version: 0.1.0
|
|
4
|
+
description: Running FastAPI development servers effectively using Uvicorn, managing production deployments with Gunicorn, and avoiding common pitfalls.
|
|
5
|
+
updated_at: 2025-10-30T17:00:00Z
|
|
6
|
+
tags: [fastapi, python, development, server, api]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# FastAPI Local Development Server
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
FastAPI is a modern, high-performance Python web framework for building APIs with automatic OpenAPI documentation, type hints, and async support. This skill covers running FastAPI development servers effectively using Uvicorn, managing production deployments with Gunicorn, and avoiding common pitfalls with process managers like PM2.
|
|
14
|
+
|
|
15
|
+
## When to Use This Skill
|
|
16
|
+
|
|
17
|
+
- Setting up FastAPI development environment with auto-reload
|
|
18
|
+
- Configuring Uvicorn for development and production
|
|
19
|
+
- Troubleshooting file watching and auto-reload issues
|
|
20
|
+
- Managing FastAPI with systemd vs PM2
|
|
21
|
+
- Resolving virtual environment and dependency issues
|
|
22
|
+
- Optimizing worker processes for production
|
|
23
|
+
- Debugging reload failures and import errors
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Development Server
|
|
28
|
+
|
|
29
|
+
**Basic Uvicorn Development:**
|
|
30
|
+
```bash
|
|
31
|
+
# Direct uvicorn
|
|
32
|
+
uvicorn main:app --reload
|
|
33
|
+
|
|
34
|
+
# With custom host and port
|
|
35
|
+
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
|
36
|
+
|
|
37
|
+
# With log level
|
|
38
|
+
uvicorn main:app --reload --log-level debug
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Using Python Module:**
|
|
42
|
+
```bash
|
|
43
|
+
# From project root
|
|
44
|
+
python -m uvicorn app.main:app --reload
|
|
45
|
+
|
|
46
|
+
# With path specification
|
|
47
|
+
PYTHONPATH=. uvicorn app.main:app --reload
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**In Python Code:**
|
|
51
|
+
```python
|
|
52
|
+
# main.py
|
|
53
|
+
import uvicorn
|
|
54
|
+
from fastapi import FastAPI
|
|
55
|
+
|
|
56
|
+
app = FastAPI()
|
|
57
|
+
|
|
58
|
+
@app.get("/")
|
|
59
|
+
def read_root():
|
|
60
|
+
return {"message": "Hello World"}
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
uvicorn.run(
|
|
64
|
+
"main:app",
|
|
65
|
+
host="0.0.0.0",
|
|
66
|
+
port=8000,
|
|
67
|
+
reload=True,
|
|
68
|
+
reload_dirs=["./app"]
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### With Gunicorn (Production)
|
|
73
|
+
|
|
74
|
+
**Gunicorn + Uvicorn Workers:**
|
|
75
|
+
```bash
|
|
76
|
+
# Production server with 4 workers
|
|
77
|
+
gunicorn app.main:app \
|
|
78
|
+
--workers 4 \
|
|
79
|
+
--worker-class uvicorn.workers.UvicornWorker \
|
|
80
|
+
--bind 0.0.0.0:8000 \
|
|
81
|
+
--timeout 120 \
|
|
82
|
+
--graceful-timeout 30
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Gunicorn Configuration File:**
|
|
86
|
+
```python
|
|
87
|
+
# gunicorn_conf.py
|
|
88
|
+
import multiprocessing
|
|
89
|
+
|
|
90
|
+
# Server socket
|
|
91
|
+
bind = "0.0.0.0:8000"
|
|
92
|
+
backlog = 2048
|
|
93
|
+
|
|
94
|
+
# Worker processes
|
|
95
|
+
workers = multiprocessing.cpu_count() * 2 + 1
|
|
96
|
+
worker_class = "uvicorn.workers.UvicornWorker"
|
|
97
|
+
worker_connections = 1000
|
|
98
|
+
timeout = 120
|
|
99
|
+
keepalive = 5
|
|
100
|
+
graceful_timeout = 30
|
|
101
|
+
|
|
102
|
+
# Logging
|
|
103
|
+
accesslog = "./logs/access.log"
|
|
104
|
+
errorlog = "./logs/error.log"
|
|
105
|
+
loglevel = "info"
|
|
106
|
+
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
|
|
107
|
+
|
|
108
|
+
# Process naming
|
|
109
|
+
proc_name = "fastapi-app"
|
|
110
|
+
|
|
111
|
+
# Server mechanics
|
|
112
|
+
daemon = False
|
|
113
|
+
pidfile = "./gunicorn.pid"
|
|
114
|
+
preload_app = True
|
|
115
|
+
|
|
116
|
+
# Worker lifecycle
|
|
117
|
+
max_requests = 1000
|
|
118
|
+
max_requests_jitter = 50
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run with config:
|
|
122
|
+
```bash
|
|
123
|
+
gunicorn -c gunicorn_conf.py app.main:app
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### With Docker
|
|
127
|
+
|
|
128
|
+
**Development Dockerfile:**
|
|
129
|
+
```dockerfile
|
|
130
|
+
FROM python:3.11-slim
|
|
131
|
+
|
|
132
|
+
WORKDIR /app
|
|
133
|
+
|
|
134
|
+
# Install dependencies
|
|
135
|
+
COPY requirements.txt .
|
|
136
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
137
|
+
|
|
138
|
+
COPY . .
|
|
139
|
+
|
|
140
|
+
# Development: auto-reload enabled
|
|
141
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Production Dockerfile:**
|
|
145
|
+
```dockerfile
|
|
146
|
+
FROM python:3.11-slim
|
|
147
|
+
|
|
148
|
+
WORKDIR /app
|
|
149
|
+
|
|
150
|
+
# Install dependencies
|
|
151
|
+
COPY requirements.txt .
|
|
152
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
153
|
+
|
|
154
|
+
COPY . .
|
|
155
|
+
|
|
156
|
+
# Production: Gunicorn with Uvicorn workers
|
|
157
|
+
CMD ["gunicorn", "app.main:app", \
|
|
158
|
+
"--workers", "4", \
|
|
159
|
+
"--worker-class", "uvicorn.workers.UvicornWorker", \
|
|
160
|
+
"--bind", "0.0.0.0:8000"]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Docker Compose:**
|
|
164
|
+
```yaml
|
|
165
|
+
version: '3.8'
|
|
166
|
+
|
|
167
|
+
services:
|
|
168
|
+
api:
|
|
169
|
+
build: .
|
|
170
|
+
ports:
|
|
171
|
+
- "8000:8000"
|
|
172
|
+
volumes:
|
|
173
|
+
- .:/app
|
|
174
|
+
environment:
|
|
175
|
+
- PYTHONUNBUFFERED=1
|
|
176
|
+
- DATABASE_URL=postgresql://user:pass@db:5432/dbname
|
|
177
|
+
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
|
178
|
+
depends_on:
|
|
179
|
+
- db
|
|
180
|
+
|
|
181
|
+
db:
|
|
182
|
+
image: postgres:15-alpine
|
|
183
|
+
environment:
|
|
184
|
+
- POSTGRES_USER=user
|
|
185
|
+
- POSTGRES_PASSWORD=pass
|
|
186
|
+
- POSTGRES_DB=dbname
|
|
187
|
+
ports:
|
|
188
|
+
- "5432:5432"
|
|
189
|
+
volumes:
|
|
190
|
+
- postgres_data:/var/lib/postgresql/data
|
|
191
|
+
|
|
192
|
+
volumes:
|
|
193
|
+
postgres_data:
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Configuration Patterns
|
|
197
|
+
|
|
198
|
+
### Uvicorn Auto-Reload Configuration
|
|
199
|
+
|
|
200
|
+
**Command Line Options:**
|
|
201
|
+
```bash
|
|
202
|
+
uvicorn main:app \
|
|
203
|
+
--reload \ # Enable auto-reload
|
|
204
|
+
--reload-dir ./app \ # Watch specific directory
|
|
205
|
+
--reload-exclude ./tests \ # Exclude directory
|
|
206
|
+
--reload-include '*.py' \ # Watch specific patterns
|
|
207
|
+
--host 0.0.0.0 \ # Listen on all interfaces
|
|
208
|
+
--port 8000 \ # Port
|
|
209
|
+
--log-level info \ # Logging level
|
|
210
|
+
--access-log \ # Enable access logs
|
|
211
|
+
--use-colors # Colored output
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Programmatic Configuration:**
|
|
215
|
+
```python
|
|
216
|
+
# main.py
|
|
217
|
+
import uvicorn
|
|
218
|
+
from fastapi import FastAPI
|
|
219
|
+
|
|
220
|
+
app = FastAPI()
|
|
221
|
+
|
|
222
|
+
if __name__ == "__main__":
|
|
223
|
+
uvicorn.run(
|
|
224
|
+
"main:app",
|
|
225
|
+
host="0.0.0.0",
|
|
226
|
+
port=8000,
|
|
227
|
+
reload=True,
|
|
228
|
+
reload_dirs=["./app", "./config"],
|
|
229
|
+
reload_excludes=["./tests", "./docs"],
|
|
230
|
+
reload_includes=["*.py", "*.yaml"],
|
|
231
|
+
log_level="info",
|
|
232
|
+
access_log=True,
|
|
233
|
+
workers=1 # Must be 1 for reload mode
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### systemd vs PM2 Comparison
|
|
238
|
+
|
|
239
|
+
**systemd (Recommended for Linux Production):**
|
|
240
|
+
|
|
241
|
+
Advantages:
|
|
242
|
+
- Native OS-level process management
|
|
243
|
+
- Automatic restart on failure
|
|
244
|
+
- System resource limits (CPU, memory)
|
|
245
|
+
- Integrated logging with journald
|
|
246
|
+
- No additional dependencies
|
|
247
|
+
- Better security controls
|
|
248
|
+
|
|
249
|
+
Example systemd service:
|
|
250
|
+
```ini
|
|
251
|
+
# /etc/systemd/system/fastapi.service
|
|
252
|
+
[Unit]
|
|
253
|
+
Description=FastAPI Application
|
|
254
|
+
After=network.target
|
|
255
|
+
|
|
256
|
+
[Service]
|
|
257
|
+
Type=notify
|
|
258
|
+
User=www-data
|
|
259
|
+
Group=www-data
|
|
260
|
+
WorkingDirectory=/opt/fastapi-app
|
|
261
|
+
Environment="PATH=/opt/fastapi-app/venv/bin"
|
|
262
|
+
ExecStart=/opt/fastapi-app/venv/bin/gunicorn \
|
|
263
|
+
-c /opt/fastapi-app/gunicorn_conf.py \
|
|
264
|
+
app.main:app
|
|
265
|
+
|
|
266
|
+
Restart=always
|
|
267
|
+
RestartSec=10
|
|
268
|
+
|
|
269
|
+
# Security
|
|
270
|
+
NoNewPrivileges=true
|
|
271
|
+
PrivateTmp=true
|
|
272
|
+
|
|
273
|
+
# Resource limits
|
|
274
|
+
LimitNOFILE=65536
|
|
275
|
+
MemoryLimit=2G
|
|
276
|
+
|
|
277
|
+
[Install]
|
|
278
|
+
WantedBy=multi-user.target
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Commands:
|
|
282
|
+
```bash
|
|
283
|
+
sudo systemctl start fastapi
|
|
284
|
+
sudo systemctl enable fastapi
|
|
285
|
+
sudo systemctl status fastapi
|
|
286
|
+
sudo journalctl -u fastapi -f
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**PM2 (Cross-Platform Alternative):**
|
|
290
|
+
|
|
291
|
+
Advantages:
|
|
292
|
+
- Works on Windows, macOS, Linux
|
|
293
|
+
- Built-in load balancer
|
|
294
|
+
- Easy monitoring dashboard
|
|
295
|
+
- Simple deployment workflow
|
|
296
|
+
|
|
297
|
+
Disadvantages for Python:
|
|
298
|
+
- Watch mode can break Python imports
|
|
299
|
+
- Less efficient than systemd on Linux
|
|
300
|
+
- Additional Node.js dependency
|
|
301
|
+
- More complex setup for Python
|
|
302
|
+
|
|
303
|
+
**Critical Warning: Never use PM2 watch mode with Python FastAPI applications.**
|
|
304
|
+
|
|
305
|
+
### PM2 Configuration (Without Watch Mode)
|
|
306
|
+
|
|
307
|
+
```javascript
|
|
308
|
+
// ecosystem.config.js
|
|
309
|
+
module.exports = {
|
|
310
|
+
apps: [{
|
|
311
|
+
name: 'fastapi-app',
|
|
312
|
+
script: '/opt/fastapi-app/venv/bin/gunicorn',
|
|
313
|
+
args: '-c gunicorn_conf.py app.main:app',
|
|
314
|
+
cwd: '/opt/fastapi-app',
|
|
315
|
+
instances: 1,
|
|
316
|
+
exec_mode: 'fork', // NOT cluster for Python
|
|
317
|
+
autorestart: true,
|
|
318
|
+
max_memory_restart: '1G',
|
|
319
|
+
|
|
320
|
+
// CRITICAL: No watch mode for Python
|
|
321
|
+
watch: false,
|
|
322
|
+
|
|
323
|
+
env: {
|
|
324
|
+
NODE_ENV: 'production',
|
|
325
|
+
PYTHONUNBUFFERED: '1'
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
error_file: './logs/pm2-error.log',
|
|
329
|
+
out_file: './logs/pm2-out.log',
|
|
330
|
+
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
|
|
331
|
+
merge_logs: true,
|
|
332
|
+
|
|
333
|
+
// Graceful shutdown
|
|
334
|
+
kill_timeout: 5000,
|
|
335
|
+
wait_ready: true,
|
|
336
|
+
listen_timeout: 10000
|
|
337
|
+
}]
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Start with PM2:**
|
|
342
|
+
```bash
|
|
343
|
+
pm2 start ecosystem.config.js
|
|
344
|
+
pm2 save
|
|
345
|
+
pm2 startup # Enable auto-start on boot
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Virtual Environment Handling
|
|
349
|
+
|
|
350
|
+
**Activate Virtual Environment:**
|
|
351
|
+
```bash
|
|
352
|
+
# Create venv
|
|
353
|
+
python -m venv venv
|
|
354
|
+
|
|
355
|
+
# Activate
|
|
356
|
+
source venv/bin/activate # Linux/macOS
|
|
357
|
+
venv\Scripts\activate # Windows
|
|
358
|
+
|
|
359
|
+
# Install dependencies
|
|
360
|
+
pip install -r requirements.txt
|
|
361
|
+
|
|
362
|
+
# Run server
|
|
363
|
+
uvicorn main:app --reload
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Direct Execution (Without Activation):**
|
|
367
|
+
```bash
|
|
368
|
+
# Use full path to venv python
|
|
369
|
+
./venv/bin/python -m uvicorn main:app --reload
|
|
370
|
+
|
|
371
|
+
# Or venv uvicorn directly
|
|
372
|
+
./venv/bin/uvicorn main:app --reload
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**In Scripts:**
|
|
376
|
+
```bash
|
|
377
|
+
#!/bin/bash
|
|
378
|
+
# start-dev.sh
|
|
379
|
+
|
|
380
|
+
# Activate venv and run
|
|
381
|
+
source venv/bin/activate
|
|
382
|
+
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Environment Variables for Imports:**
|
|
386
|
+
```bash
|
|
387
|
+
# Set PYTHONPATH for imports
|
|
388
|
+
PYTHONPATH=/opt/fastapi-app ./venv/bin/uvicorn app.main:app --reload
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### WSL WATCHFILES_FORCE_POLLING
|
|
392
|
+
|
|
393
|
+
**Problem:** File watching doesn't work in WSL (Windows Subsystem for Linux) due to filesystem event limitations.
|
|
394
|
+
|
|
395
|
+
**Solution:** Force polling mode for file changes.
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
# Set environment variable
|
|
399
|
+
export WATCHFILES_FORCE_POLLING=true
|
|
400
|
+
|
|
401
|
+
# Run uvicorn
|
|
402
|
+
uvicorn main:app --reload
|
|
403
|
+
|
|
404
|
+
# Or inline
|
|
405
|
+
WATCHFILES_FORCE_POLLING=true uvicorn main:app --reload
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Permanent Configuration (.bashrc or .zshrc):**
|
|
409
|
+
```bash
|
|
410
|
+
# Add to shell config
|
|
411
|
+
echo 'export WATCHFILES_FORCE_POLLING=true' >> ~/.bashrc
|
|
412
|
+
source ~/.bashrc
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**In Python Code:**
|
|
416
|
+
```python
|
|
417
|
+
import os
|
|
418
|
+
os.environ['WATCHFILES_FORCE_POLLING'] = 'true'
|
|
419
|
+
|
|
420
|
+
import uvicorn
|
|
421
|
+
|
|
422
|
+
if __name__ == "__main__":
|
|
423
|
+
uvicorn.run("main:app", reload=True)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Reload Directory Configuration
|
|
427
|
+
|
|
428
|
+
**Watch Specific Directories:**
|
|
429
|
+
```bash
|
|
430
|
+
# Single directory
|
|
431
|
+
uvicorn main:app --reload --reload-dir ./app
|
|
432
|
+
|
|
433
|
+
# Multiple directories
|
|
434
|
+
uvicorn main:app --reload \
|
|
435
|
+
--reload-dir ./app \
|
|
436
|
+
--reload-dir ./config \
|
|
437
|
+
--reload-dir ./models
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Exclude Directories:**
|
|
441
|
+
```bash
|
|
442
|
+
uvicorn main:app --reload \
|
|
443
|
+
--reload-dir ./app \
|
|
444
|
+
--reload-exclude ./app/tests \
|
|
445
|
+
--reload-exclude ./app/migrations
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Include Specific File Patterns:**
|
|
449
|
+
```bash
|
|
450
|
+
uvicorn main:app --reload \
|
|
451
|
+
--reload-include '*.py' \
|
|
452
|
+
--reload-include '*.yaml' \
|
|
453
|
+
--reload-include '*.json'
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**In Code:**
|
|
457
|
+
```python
|
|
458
|
+
import uvicorn
|
|
459
|
+
|
|
460
|
+
if __name__ == "__main__":
|
|
461
|
+
uvicorn.run(
|
|
462
|
+
"main:app",
|
|
463
|
+
reload=True,
|
|
464
|
+
reload_dirs=["./app", "./config"],
|
|
465
|
+
reload_excludes=["./tests", "./migrations"],
|
|
466
|
+
reload_includes=["*.py", "*.yaml"]
|
|
467
|
+
)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Worker Management
|
|
471
|
+
|
|
472
|
+
**Development:** Always use 1 worker with reload.
|
|
473
|
+
```bash
|
|
474
|
+
uvicorn main:app --reload --workers 1
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Production:** Calculate workers based on CPU cores.
|
|
478
|
+
|
|
479
|
+
**Formula:** `(2 × CPU cores) + 1`
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
# 4 CPU cores = 9 workers
|
|
483
|
+
gunicorn app.main:app \
|
|
484
|
+
--workers 9 \
|
|
485
|
+
--worker-class uvicorn.workers.UvicornWorker
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Dynamic Worker Calculation:**
|
|
489
|
+
```python
|
|
490
|
+
# gunicorn_conf.py
|
|
491
|
+
import multiprocessing
|
|
492
|
+
|
|
493
|
+
workers = multiprocessing.cpu_count() * 2 + 1
|
|
494
|
+
worker_class = "uvicorn.workers.UvicornWorker"
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**Worker Timeout Configuration:**
|
|
498
|
+
```python
|
|
499
|
+
# gunicorn_conf.py
|
|
500
|
+
timeout = 120 # Worker timeout (seconds)
|
|
501
|
+
graceful_timeout = 30 # Graceful shutdown time
|
|
502
|
+
keepalive = 5 # Keep-alive seconds
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Framework-Specific Best Practices
|
|
506
|
+
|
|
507
|
+
### Dependency Injection
|
|
508
|
+
|
|
509
|
+
FastAPI's dependency injection system:
|
|
510
|
+
|
|
511
|
+
```python
|
|
512
|
+
from fastapi import Depends, FastAPI
|
|
513
|
+
from sqlalchemy.orm import Session
|
|
514
|
+
|
|
515
|
+
app = FastAPI()
|
|
516
|
+
|
|
517
|
+
# Database dependency
|
|
518
|
+
def get_db():
|
|
519
|
+
db = SessionLocal()
|
|
520
|
+
try:
|
|
521
|
+
yield db
|
|
522
|
+
finally:
|
|
523
|
+
db.close()
|
|
524
|
+
|
|
525
|
+
@app.get("/users/{user_id}")
|
|
526
|
+
def read_user(user_id: int, db: Session = Depends(get_db)):
|
|
527
|
+
return db.query(User).filter(User.id == user_id).first()
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Benefit for Development:**
|
|
531
|
+
- Dependencies are reloaded automatically
|
|
532
|
+
- Easy mocking for tests
|
|
533
|
+
- Clean separation of concerns
|
|
534
|
+
|
|
535
|
+
### Async Operations
|
|
536
|
+
|
|
537
|
+
**Use async for I/O-bound operations:**
|
|
538
|
+
|
|
539
|
+
```python
|
|
540
|
+
from fastapi import FastAPI
|
|
541
|
+
import httpx
|
|
542
|
+
|
|
543
|
+
app = FastAPI()
|
|
544
|
+
|
|
545
|
+
@app.get("/external")
|
|
546
|
+
async def call_external():
|
|
547
|
+
async with httpx.AsyncClient() as client:
|
|
548
|
+
response = await client.get("https://api.example.com")
|
|
549
|
+
return response.json()
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**Mixed sync/async endpoints:**
|
|
553
|
+
|
|
554
|
+
```python
|
|
555
|
+
# Sync endpoint (blocking)
|
|
556
|
+
@app.get("/sync")
|
|
557
|
+
def sync_endpoint():
|
|
558
|
+
result = blocking_operation()
|
|
559
|
+
return result
|
|
560
|
+
|
|
561
|
+
# Async endpoint (non-blocking)
|
|
562
|
+
@app.get("/async")
|
|
563
|
+
async def async_endpoint():
|
|
564
|
+
result = await async_operation()
|
|
565
|
+
return result
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**Worker configuration for async:**
|
|
569
|
+
```python
|
|
570
|
+
# gunicorn_conf.py
|
|
571
|
+
# Use UvicornWorker for async support
|
|
572
|
+
worker_class = "uvicorn.workers.UvicornWorker"
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Lifespan Events
|
|
576
|
+
|
|
577
|
+
**Startup and shutdown logic:**
|
|
578
|
+
|
|
579
|
+
```python
|
|
580
|
+
from contextlib import asynccontextmanager
|
|
581
|
+
from fastapi import FastAPI
|
|
582
|
+
|
|
583
|
+
@asynccontextmanager
|
|
584
|
+
async def lifespan(app: FastAPI):
|
|
585
|
+
# Startup
|
|
586
|
+
print("Starting up...")
|
|
587
|
+
# Initialize database pool, cache connections, etc.
|
|
588
|
+
yield
|
|
589
|
+
# Shutdown
|
|
590
|
+
print("Shutting down...")
|
|
591
|
+
# Close database pool, cleanup resources
|
|
592
|
+
|
|
593
|
+
app = FastAPI(lifespan=lifespan)
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Why important for development:**
|
|
597
|
+
- Clean resource management
|
|
598
|
+
- Proper connection pooling
|
|
599
|
+
- Graceful reload on code changes
|
|
600
|
+
|
|
601
|
+
### Static Files and Templates
|
|
602
|
+
|
|
603
|
+
**Serve static files:**
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
from fastapi import FastAPI
|
|
607
|
+
from fastapi.staticfiles import StaticFiles
|
|
608
|
+
|
|
609
|
+
app = FastAPI()
|
|
610
|
+
|
|
611
|
+
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Templates with Jinja2:**
|
|
615
|
+
|
|
616
|
+
```python
|
|
617
|
+
from fastapi import FastAPI, Request
|
|
618
|
+
from fastapi.templating import Jinja2Templates
|
|
619
|
+
|
|
620
|
+
app = FastAPI()
|
|
621
|
+
templates = Jinja2Templates(directory="templates")
|
|
622
|
+
|
|
623
|
+
@app.get("/")
|
|
624
|
+
def read_root(request: Request):
|
|
625
|
+
return templates.TemplateResponse("index.html", {"request": request})
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**Auto-reload includes templates and static files** when using `--reload-dir`.
|
|
629
|
+
|
|
630
|
+
## Common Problems & Solutions
|
|
631
|
+
|
|
632
|
+
### Problem 1: Port Already in Use (EADDRINUSE)
|
|
633
|
+
|
|
634
|
+
**Symptoms:**
|
|
635
|
+
```
|
|
636
|
+
ERROR: [Errno 48] error while attempting to bind on address ('0.0.0.0', 8000): address already in use
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Root Cause:**
|
|
640
|
+
Another process (previous Uvicorn instance, other server, or zombie process) is using port 8000.
|
|
641
|
+
|
|
642
|
+
**Solution:**
|
|
643
|
+
|
|
644
|
+
**Option A: Find and Kill Process**
|
|
645
|
+
```bash
|
|
646
|
+
# Linux/macOS
|
|
647
|
+
lsof -ti:8000 | xargs kill -9
|
|
648
|
+
|
|
649
|
+
# Alternative with fuser
|
|
650
|
+
fuser -k 8000/tcp
|
|
651
|
+
|
|
652
|
+
# Windows
|
|
653
|
+
netstat -ano | findstr :8000
|
|
654
|
+
taskkill /PID <PID> /F
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
**Option B: Use Different Port**
|
|
658
|
+
```bash
|
|
659
|
+
uvicorn main:app --reload --port 8001
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
**Option C: Cleanup Script**
|
|
663
|
+
```bash
|
|
664
|
+
#!/bin/bash
|
|
665
|
+
# kill-and-start.sh
|
|
666
|
+
|
|
667
|
+
# Kill any process on port 8000
|
|
668
|
+
lsof -ti:8000 | xargs kill -9 2>/dev/null || true
|
|
669
|
+
|
|
670
|
+
# Wait a moment
|
|
671
|
+
sleep 1
|
|
672
|
+
|
|
673
|
+
# Start server
|
|
674
|
+
uvicorn main:app --reload --port 8000
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**Option D: Dynamic Port Selection**
|
|
678
|
+
```python
|
|
679
|
+
import uvicorn
|
|
680
|
+
import socket
|
|
681
|
+
|
|
682
|
+
def find_free_port():
|
|
683
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
684
|
+
s.bind(('', 0))
|
|
685
|
+
s.listen(1)
|
|
686
|
+
port = s.getsockname()[1]
|
|
687
|
+
return port
|
|
688
|
+
|
|
689
|
+
if __name__ == "__main__":
|
|
690
|
+
port = find_free_port()
|
|
691
|
+
print(f"Starting server on port {port}")
|
|
692
|
+
uvicorn.run("main:app", host="0.0.0.0", port=port, reload=True)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Problem 2: Auto-Reload Not Working
|
|
696
|
+
|
|
697
|
+
**Symptoms:**
|
|
698
|
+
- Code changes don't trigger server restart
|
|
699
|
+
- Need to manually restart Uvicorn
|
|
700
|
+
- No "Reload detected" messages in console
|
|
701
|
+
|
|
702
|
+
**Root Cause:**
|
|
703
|
+
Multiple causes: file watching disabled, wrong reload directory, WSL filesystem issues, or PM2 interference.
|
|
704
|
+
|
|
705
|
+
**Solution:**
|
|
706
|
+
|
|
707
|
+
**Step 1: Verify Reload is Enabled**
|
|
708
|
+
```bash
|
|
709
|
+
# Ensure --reload flag is present
|
|
710
|
+
uvicorn main:app --reload
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
**Step 2: Check Reload Directories**
|
|
714
|
+
```bash
|
|
715
|
+
# Explicitly set reload directory
|
|
716
|
+
uvicorn main:app --reload --reload-dir ./app
|
|
717
|
+
|
|
718
|
+
# Include specific file patterns
|
|
719
|
+
uvicorn main:app --reload --reload-include '*.py'
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
**Step 3: WSL Force Polling**
|
|
723
|
+
```bash
|
|
724
|
+
# For WSL on Windows
|
|
725
|
+
WATCHFILES_FORCE_POLLING=true uvicorn main:app --reload
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
**Step 4: Check File Permissions**
|
|
729
|
+
```bash
|
|
730
|
+
# Ensure files are readable
|
|
731
|
+
chmod -R 644 ./app/*.py
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
**Step 5: Disable PM2 Watch**
|
|
735
|
+
|
|
736
|
+
If using PM2 (not recommended for dev):
|
|
737
|
+
```javascript
|
|
738
|
+
// ecosystem.config.js
|
|
739
|
+
module.exports = {
|
|
740
|
+
apps: [{
|
|
741
|
+
name: 'fastapi',
|
|
742
|
+
script: 'venv/bin/uvicorn',
|
|
743
|
+
args: 'main:app --reload',
|
|
744
|
+
watch: false // CRITICAL: must be false
|
|
745
|
+
}]
|
|
746
|
+
};
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**Step 6: Verify No Syntax Errors**
|
|
750
|
+
|
|
751
|
+
Syntax errors can break auto-reload:
|
|
752
|
+
```bash
|
|
753
|
+
# Test import
|
|
754
|
+
python -c "from app.main import app"
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**Step 7: Check Uvicorn Version**
|
|
758
|
+
```bash
|
|
759
|
+
# Update uvicorn
|
|
760
|
+
pip install --upgrade uvicorn[standard]
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Problem 3: Import Errors After Reload
|
|
764
|
+
|
|
765
|
+
**Symptoms:**
|
|
766
|
+
```
|
|
767
|
+
ModuleNotFoundError: No module named 'app'
|
|
768
|
+
ImportError: attempted relative import with no known parent package
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Root Cause:**
|
|
772
|
+
Incorrect PYTHONPATH, wrong working directory, or circular imports.
|
|
773
|
+
|
|
774
|
+
**Solution:**
|
|
775
|
+
|
|
776
|
+
**Step 1: Set PYTHONPATH**
|
|
777
|
+
```bash
|
|
778
|
+
# Add current directory to Python path
|
|
779
|
+
export PYTHONPATH="${PYTHONPATH}:$(pwd)"
|
|
780
|
+
uvicorn app.main:app --reload
|
|
781
|
+
|
|
782
|
+
# Or inline
|
|
783
|
+
PYTHONPATH=. uvicorn app.main:app --reload
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Step 2: Use Python Module Execution**
|
|
787
|
+
```bash
|
|
788
|
+
# Run as module from project root
|
|
789
|
+
python -m uvicorn app.main:app --reload
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**Step 3: Fix Import Paths**
|
|
793
|
+
|
|
794
|
+
Bad:
|
|
795
|
+
```python
|
|
796
|
+
# Relative import without parent
|
|
797
|
+
from models import User
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Good:
|
|
801
|
+
```python
|
|
802
|
+
# Absolute import
|
|
803
|
+
from app.models import User
|
|
804
|
+
|
|
805
|
+
# Or relative with parent
|
|
806
|
+
from .models import User
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
**Step 4: Check Directory Structure**
|
|
810
|
+
```
|
|
811
|
+
fastapi-project/
|
|
812
|
+
├── app/
|
|
813
|
+
│ ├── __init__.py ← REQUIRED for package
|
|
814
|
+
│ ├── main.py
|
|
815
|
+
│ └── models.py
|
|
816
|
+
├── requirements.txt
|
|
817
|
+
└── main.py (or run from app.main)
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
**Step 5: Avoid Circular Imports**
|
|
821
|
+
|
|
822
|
+
Bad:
|
|
823
|
+
```python
|
|
824
|
+
# a.py
|
|
825
|
+
from b import function_b
|
|
826
|
+
|
|
827
|
+
# b.py
|
|
828
|
+
from a import function_a # Circular!
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
Good:
|
|
832
|
+
```python
|
|
833
|
+
# Use dependency injection or delayed imports
|
|
834
|
+
# a.py
|
|
835
|
+
def function_a():
|
|
836
|
+
from b import function_b # Import inside function
|
|
837
|
+
function_b()
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### Problem 4: PM2 Watch Mode Breaks Application
|
|
841
|
+
|
|
842
|
+
**Symptoms:**
|
|
843
|
+
- Application restarts continuously
|
|
844
|
+
- Import errors appear after file changes
|
|
845
|
+
- Worker processes crash unexpectedly
|
|
846
|
+
|
|
847
|
+
**Root Cause:**
|
|
848
|
+
PM2 watch mode interferes with Python's import system and virtual environment, causing module resolution failures.
|
|
849
|
+
|
|
850
|
+
**Solution:**
|
|
851
|
+
|
|
852
|
+
**NEVER use PM2 watch mode with FastAPI/Python applications.**
|
|
853
|
+
|
|
854
|
+
**Wrong:**
|
|
855
|
+
```javascript
|
|
856
|
+
// ecosystem.config.js
|
|
857
|
+
module.exports = {
|
|
858
|
+
apps: [{
|
|
859
|
+
name: 'fastapi',
|
|
860
|
+
script: 'venv/bin/uvicorn',
|
|
861
|
+
args: 'main:app --reload',
|
|
862
|
+
watch: true, // WRONG - Breaks Python
|
|
863
|
+
ignore_watch: ['logs', '.git']
|
|
864
|
+
}]
|
|
865
|
+
};
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
**Correct for Development:**
|
|
869
|
+
|
|
870
|
+
Don't use PM2 at all. Use uvicorn directly:
|
|
871
|
+
```bash
|
|
872
|
+
uvicorn main:app --reload
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
**Correct for Production:**
|
|
876
|
+
|
|
877
|
+
Use PM2 without watch mode:
|
|
878
|
+
```javascript
|
|
879
|
+
// ecosystem.config.js
|
|
880
|
+
module.exports = {
|
|
881
|
+
apps: [{
|
|
882
|
+
name: 'fastapi',
|
|
883
|
+
script: 'venv/bin/gunicorn',
|
|
884
|
+
args: '-c gunicorn_conf.py app.main:app',
|
|
885
|
+
watch: false, // Correct - no watch mode
|
|
886
|
+
autorestart: true
|
|
887
|
+
}]
|
|
888
|
+
};
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
**Alternative: Use systemd for production on Linux.**
|
|
892
|
+
|
|
893
|
+
### Problem 5: Workers Timing Out Under Load
|
|
894
|
+
|
|
895
|
+
**Symptoms:**
|
|
896
|
+
```
|
|
897
|
+
[CRITICAL] WORKER TIMEOUT (pid:12345)
|
|
898
|
+
Worker with pid 12345 was terminated due to signal 9
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
**Root Cause:**
|
|
902
|
+
Worker timeout too short for slow operations, insufficient workers, or blocking operations in async context.
|
|
903
|
+
|
|
904
|
+
**Solution:**
|
|
905
|
+
|
|
906
|
+
**Step 1: Increase Timeout**
|
|
907
|
+
```python
|
|
908
|
+
# gunicorn_conf.py
|
|
909
|
+
timeout = 300 # 5 minutes for long operations
|
|
910
|
+
graceful_timeout = 60
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Step 2: Optimize Worker Count**
|
|
914
|
+
```python
|
|
915
|
+
# gunicorn_conf.py
|
|
916
|
+
import multiprocessing
|
|
917
|
+
|
|
918
|
+
# Increase workers for CPU-bound tasks
|
|
919
|
+
workers = multiprocessing.cpu_count() * 2 + 1
|
|
920
|
+
|
|
921
|
+
# Or set explicitly
|
|
922
|
+
workers = 8
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**Step 3: Use Async for I/O Operations**
|
|
926
|
+
|
|
927
|
+
Bad (blocks worker):
|
|
928
|
+
```python
|
|
929
|
+
import requests
|
|
930
|
+
|
|
931
|
+
@app.get("/data")
|
|
932
|
+
def get_data():
|
|
933
|
+
response = requests.get("https://slow-api.com") # Blocks!
|
|
934
|
+
return response.json()
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
Good (non-blocking):
|
|
938
|
+
```python
|
|
939
|
+
import httpx
|
|
940
|
+
|
|
941
|
+
@app.get("/data")
|
|
942
|
+
async def get_data():
|
|
943
|
+
async with httpx.AsyncClient() as client:
|
|
944
|
+
response = await client.get("https://slow-api.com")
|
|
945
|
+
return response.json()
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
**Step 4: Background Tasks**
|
|
949
|
+
|
|
950
|
+
For long-running operations:
|
|
951
|
+
```python
|
|
952
|
+
from fastapi import BackgroundTasks
|
|
953
|
+
|
|
954
|
+
def process_data(data):
|
|
955
|
+
# Long-running task
|
|
956
|
+
pass
|
|
957
|
+
|
|
958
|
+
@app.post("/process")
|
|
959
|
+
async def trigger_process(background_tasks: BackgroundTasks, data: dict):
|
|
960
|
+
background_tasks.add_task(process_data, data)
|
|
961
|
+
return {"status": "processing"}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Step 5: Monitor Worker Health**
|
|
965
|
+
```bash
|
|
966
|
+
# Check worker status
|
|
967
|
+
ps aux | grep gunicorn
|
|
968
|
+
|
|
969
|
+
# Monitor logs
|
|
970
|
+
tail -f logs/error.log
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
## Anti-Patterns
|
|
974
|
+
|
|
975
|
+
### What NOT to Do
|
|
976
|
+
|
|
977
|
+
**1. Never Use PM2 Watch Mode with Python**
|
|
978
|
+
```javascript
|
|
979
|
+
// WRONG
|
|
980
|
+
module.exports = {
|
|
981
|
+
apps: [{
|
|
982
|
+
script: 'venv/bin/uvicorn',
|
|
983
|
+
watch: true // Breaks Python imports
|
|
984
|
+
}]
|
|
985
|
+
};
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
Why: PM2 watch triggers restarts that break Python's import system and virtual environment.
|
|
989
|
+
|
|
990
|
+
**2. Don't Use Multiple Workers with --reload**
|
|
991
|
+
```bash
|
|
992
|
+
# WRONG - reload requires single worker
|
|
993
|
+
uvicorn main:app --reload --workers 4
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
Reload mode only works with 1 worker. Use multiple workers in production without reload.
|
|
997
|
+
|
|
998
|
+
**3. Don't Run Blocking Operations in Async Endpoints**
|
|
999
|
+
```python
|
|
1000
|
+
# WRONG
|
|
1001
|
+
@app.get("/data")
|
|
1002
|
+
async def get_data():
|
|
1003
|
+
result = requests.get("https://api.com") # Blocking in async!
|
|
1004
|
+
return result.json()
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
Use `httpx` or `aiohttp` for async HTTP requests.
|
|
1008
|
+
|
|
1009
|
+
**4. Don't Forget Virtual Environment in Production**
|
|
1010
|
+
```bash
|
|
1011
|
+
# WRONG - uses system Python
|
|
1012
|
+
uvicorn main:app
|
|
1013
|
+
|
|
1014
|
+
# Correct - uses venv
|
|
1015
|
+
./venv/bin/uvicorn main:app
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
**5. Don't Skip Graceful Shutdown**
|
|
1019
|
+
```python
|
|
1020
|
+
# WRONG - abrupt termination
|
|
1021
|
+
# No cleanup code
|
|
1022
|
+
|
|
1023
|
+
# Correct - graceful shutdown
|
|
1024
|
+
@asynccontextmanager
|
|
1025
|
+
async def lifespan(app: FastAPI):
|
|
1026
|
+
yield
|
|
1027
|
+
# Cleanup: close DB, cache connections
|
|
1028
|
+
await db_pool.close()
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**6. Don't Use Development Server in Production**
|
|
1032
|
+
```bash
|
|
1033
|
+
# WRONG for production
|
|
1034
|
+
uvicorn main:app --reload
|
|
1035
|
+
|
|
1036
|
+
# Correct for production
|
|
1037
|
+
gunicorn app.main:app \
|
|
1038
|
+
--workers 4 \
|
|
1039
|
+
--worker-class uvicorn.workers.UvicornWorker
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
**7. Don't Ignore Security Headers**
|
|
1043
|
+
```python
|
|
1044
|
+
# WRONG - no security middleware
|
|
1045
|
+
app = FastAPI()
|
|
1046
|
+
|
|
1047
|
+
# Correct - add security
|
|
1048
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
1049
|
+
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
|
1050
|
+
|
|
1051
|
+
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["*.example.com"])
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
## Quick Reference
|
|
1055
|
+
|
|
1056
|
+
### Commands
|
|
1057
|
+
|
|
1058
|
+
```bash
|
|
1059
|
+
# Development
|
|
1060
|
+
uvicorn main:app --reload # Basic dev server
|
|
1061
|
+
uvicorn main:app --reload --port 8001 # Custom port
|
|
1062
|
+
uvicorn main:app --reload --reload-dir ./app # Watch specific dir
|
|
1063
|
+
python -m uvicorn app.main:app --reload # Module execution
|
|
1064
|
+
|
|
1065
|
+
# WSL
|
|
1066
|
+
WATCHFILES_FORCE_POLLING=true uvicorn main:app --reload
|
|
1067
|
+
|
|
1068
|
+
# Production
|
|
1069
|
+
gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
1070
|
+
gunicorn -c gunicorn_conf.py app.main:app
|
|
1071
|
+
|
|
1072
|
+
# Virtual Environment
|
|
1073
|
+
python -m venv venv # Create venv
|
|
1074
|
+
source venv/bin/activate # Activate (Linux/macOS)
|
|
1075
|
+
venv\Scripts\activate # Activate (Windows)
|
|
1076
|
+
./venv/bin/uvicorn main:app --reload # Direct execution
|
|
1077
|
+
|
|
1078
|
+
# Process Management
|
|
1079
|
+
lsof -ti:8000 | xargs kill -9 # Kill process on port
|
|
1080
|
+
ps aux | grep uvicorn # Find uvicorn processes
|
|
1081
|
+
|
|
1082
|
+
# systemd
|
|
1083
|
+
sudo systemctl start fastapi # Start service
|
|
1084
|
+
sudo systemctl status fastapi # Check status
|
|
1085
|
+
sudo journalctl -u fastapi -f # View logs
|
|
1086
|
+
|
|
1087
|
+
# PM2 (Production Only)
|
|
1088
|
+
pm2 start ecosystem.config.js # Start with PM2
|
|
1089
|
+
pm2 logs fastapi # View logs
|
|
1090
|
+
pm2 restart fastapi # Restart
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
### Configuration Templates
|
|
1094
|
+
|
|
1095
|
+
**requirements.txt:**
|
|
1096
|
+
```
|
|
1097
|
+
fastapi==0.104.1
|
|
1098
|
+
uvicorn[standard]==0.24.0
|
|
1099
|
+
gunicorn==21.2.0
|
|
1100
|
+
python-multipart==0.0.6
|
|
1101
|
+
pydantic==2.5.0
|
|
1102
|
+
pydantic-settings==2.1.0
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
**Minimal FastAPI App:**
|
|
1106
|
+
```python
|
|
1107
|
+
# main.py
|
|
1108
|
+
from fastapi import FastAPI
|
|
1109
|
+
import uvicorn
|
|
1110
|
+
|
|
1111
|
+
app = FastAPI()
|
|
1112
|
+
|
|
1113
|
+
@app.get("/")
|
|
1114
|
+
def read_root():
|
|
1115
|
+
return {"message": "Hello World"}
|
|
1116
|
+
|
|
1117
|
+
@app.get("/health")
|
|
1118
|
+
def health_check():
|
|
1119
|
+
return {"status": "healthy"}
|
|
1120
|
+
|
|
1121
|
+
if __name__ == "__main__":
|
|
1122
|
+
uvicorn.run(
|
|
1123
|
+
"main:app",
|
|
1124
|
+
host="0.0.0.0",
|
|
1125
|
+
port=8000,
|
|
1126
|
+
reload=True
|
|
1127
|
+
)
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
**Production Gunicorn Config:**
|
|
1131
|
+
```python
|
|
1132
|
+
# gunicorn_conf.py
|
|
1133
|
+
import multiprocessing
|
|
1134
|
+
|
|
1135
|
+
bind = "0.0.0.0:8000"
|
|
1136
|
+
workers = multiprocessing.cpu_count() * 2 + 1
|
|
1137
|
+
worker_class = "uvicorn.workers.UvicornWorker"
|
|
1138
|
+
timeout = 120
|
|
1139
|
+
graceful_timeout = 30
|
|
1140
|
+
keepalive = 5
|
|
1141
|
+
max_requests = 1000
|
|
1142
|
+
max_requests_jitter = 50
|
|
1143
|
+
|
|
1144
|
+
accesslog = "./logs/access.log"
|
|
1145
|
+
errorlog = "./logs/error.log"
|
|
1146
|
+
loglevel = "info"
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
**systemd Service:**
|
|
1150
|
+
```ini
|
|
1151
|
+
[Unit]
|
|
1152
|
+
Description=FastAPI Application
|
|
1153
|
+
After=network.target
|
|
1154
|
+
|
|
1155
|
+
[Service]
|
|
1156
|
+
Type=notify
|
|
1157
|
+
User=www-data
|
|
1158
|
+
WorkingDirectory=/opt/fastapi-app
|
|
1159
|
+
Environment="PATH=/opt/fastapi-app/venv/bin"
|
|
1160
|
+
ExecStart=/opt/fastapi-app/venv/bin/gunicorn -c gunicorn_conf.py app.main:app
|
|
1161
|
+
Restart=always
|
|
1162
|
+
RestartSec=10
|
|
1163
|
+
|
|
1164
|
+
[Install]
|
|
1165
|
+
WantedBy=multi-user.target
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
**PM2 Config (No Watch):**
|
|
1169
|
+
```javascript
|
|
1170
|
+
module.exports = {
|
|
1171
|
+
apps: [{
|
|
1172
|
+
name: 'fastapi',
|
|
1173
|
+
script: '/opt/fastapi-app/venv/bin/gunicorn',
|
|
1174
|
+
args: '-c gunicorn_conf.py app.main:app',
|
|
1175
|
+
cwd: '/opt/fastapi-app',
|
|
1176
|
+
instances: 1,
|
|
1177
|
+
exec_mode: 'fork',
|
|
1178
|
+
autorestart: true,
|
|
1179
|
+
watch: false, // CRITICAL: never true for Python
|
|
1180
|
+
max_memory_restart: '1G',
|
|
1181
|
+
env: {
|
|
1182
|
+
PYTHONUNBUFFERED: '1'
|
|
1183
|
+
}
|
|
1184
|
+
}]
|
|
1185
|
+
};
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
## Related Skills
|
|
1189
|
+
|
|
1190
|
+
- **docker-containerization** - For containerized FastAPI deployments
|
|
1191
|
+
- **systematic-debugging** - For complex debugging scenarios
|
|
1192
|
+
- **express-local-dev** - Similar patterns for Node.js/Express applications
|
|
1193
|
+
- **nextjs-local-dev** - For full-stack applications with Next.js frontend
|
|
1194
|
+
|
|
1195
|
+
---
|
|
1196
|
+
|
|
1197
|
+
**FastAPI Version Compatibility:** This skill covers FastAPI 0.100+ and Uvicorn 0.20+. For older versions, consult the official FastAPI documentation.
|
|
1198
|
+
|
|
1199
|
+
**Last Updated:** 2024 - Reflects current best practices for FastAPI development and deployment.
|