# FastAPI User Guide

## 1. Quick Start

FastAPI is a modern, high-performance Python web framework for building APIs with standard Python type hints. It generates OpenAPI 3.1.0 schemas automatically, provides interactive API documentation out of the box, and achieves performance on par with Node.js and Go thanks to its foundation on Starlette (ASGI) and Pydantic (data validation).

**Key Features:**

- Automatic OpenAPI 3.1.0 schema generation from your code
- Interactive API docs: Swagger UI (`/docs`) and ReDoc (`/redoc`) included by default
- Full data validation via Pydantic models and Python type hints
- Dependency injection system for reusable logic across endpoints
- Built-in OAuth2, JWT, API key, and HTTP auth security schemes
- WebSocket support with the same decorator pattern as HTTP routes
- Server-Sent Events (SSE) via `EventSourceResponse`
- Background task execution without blocking responses
- File upload handling with `UploadFile` and `File`
- Sub-application mounting and modular routing via `APIRouter`
- Async-first (`async def`) with full sync (`def`) support
- Automatic request/response serialization with JSON, ORJSON, UJSON

**Prerequisites:** Python 3.8+

**Minimal setup (under 2 minutes):**

```bash
pip install "fastapi[standard]"
```

Create `main.py`:

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}
```

Run the development server:

```bash
fastapi dev main.py
```

```text
INFO     Using path main.py
INFO     Resolved absolute path /home/user/app/main.py
INFO     Searching for package file structure from directories with __init__.py files
INFO     Importing from /home/user/app

 ╭─ FastAPI CLI - Development mode ─╮
 │                                   │
 │  Serving at: http://127.0.0.1:8000│
 │                                   │
 │  API docs: http://127.0.0.1:8000/docs │
 │                                   │
 │  Running in development mode, for │
 │  production use: fastapi run       │
 │                                   │
 ╰───────────────────────────────────╯
```

Open `http://127.0.0.1:8000/docs` to see interactive documentation for your API.

> [!TIP]
> The `fastapi dev` command enables auto-reload by default. Every time you save a file, the server restarts automatically, making development fast and iterative.

## 2. Installation

### Install Options

| Method | Command | What You Get |
|--------|---------|-------------|
| Standard (recommended) | `pip install "fastapi[standard]"` | FastAPI + Uvicorn + `fastapi` CLI + email validation + multipart |
| Minimal | `pip install fastapi` | FastAPI core only; bring your own ASGI server |
| With ORJSON | `pip install "fastapi[standard]" orjson` | Faster JSON serialization via `ORJSONResponse` |
| With UJSON | `pip install "fastapi[standard]" ujson` | Alternative JSON serialization via `UJSONResponse` |

### Dependency Tree

FastAPI's core dependencies (installed automatically):

| Package | Purpose |
|---------|---------|
| `starlette` | ASGI framework (routing, middleware, requests, responses) |
| `pydantic` | Data validation and serialization via type hints |
| `typing_extensions` | Backported type hint features |

The `[standard]` extra adds:

| Package | Purpose |
|---------|---------|
| `uvicorn[standard]` | ASGI server for development and production |
| `fastapi-cli` | Provides `fastapi dev` and `fastapi run` CLI commands |
| `httpx` | Async HTTP client (used with `TestClient`) |
| `jinja2` | Template rendering |
| `python-multipart` | File upload and form data parsing |
| `email-validator` | Email string validation for Pydantic models |

> [!IMPORTANT]
> If you install `fastapi` without the `[standard]` extra, the `fastapi` CLI command is not available. You will need to run your app directly with `uvicorn main:app --reload` instead of `fastapi dev main.py`.

### Verifying the Installation

```bash
python -c "import fastapi; print(fastapi.__version__)"
```

```text
0.135.3
```

```bash
fastapi --version
```

If the CLI is not installed, you will see:

```text
RuntimeError: To use the fastapi command, please install "fastapi[standard]"
```

### Running Your Application

| Environment | Command | Notes |
|-------------|---------|-------|
| Development | `fastapi dev main.py` | Auto-reload enabled, binds to `127.0.0.1` |
| Development (manual) | `uvicorn main:app --reload` | Equivalent to `fastapi dev` |
| Production | `fastapi run main.py` | No auto-reload, binds to `0.0.0.0` |
| Production (manual) | `uvicorn main:app --host 0.0.0.0 --port 8000` | Full control over server settings |
| Production (Gunicorn) | `gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker` | Multi-worker process manager |

## 3. Application Setup

FastAPI applications are configured through the `FastAPI()` constructor. There are no external configuration files or config keys; all settings are passed as Python arguments.

### FastAPI Constructor Parameters

| Parameter | Type | Default | Purpose |
|-----------|------|---------|---------|
| `title` | `str` | `"FastAPI"` | API title shown in docs |
| `summary` | `str \| None` | `None` | Short API summary for docs |
| `description` | `str` | `""` | Detailed API description (supports Markdown) |
| `version` | `str` | `"0.1.0"` | API version shown in docs and OpenAPI schema |
| `debug` | `bool` | `False` | Enable debug mode (detailed error responses) |
| `openapi_url` | `str \| None` | `"/openapi.json"` | OpenAPI schema endpoint; set to `None` to disable |
| `docs_url` | `str \| None` | `"/docs"` | Swagger UI endpoint; set to `None` to disable |
| `redoc_url` | `str \| None` | `"/redoc"` | ReDoc endpoint; set to `None` to disable |
| `default_response_class` | `type[Response]` | `JSONResponse` | Default response class for all endpoints |
| `redirect_slashes` | `bool` | `True` | Auto-redirect `/items` to `/items/` (or vice versa) |
| `root_path` | `str` | `""` | ASGI root path for reverse proxies |
| `root_path_in_servers` | `bool` | `True` | Include root_path in OpenAPI servers list |
| `lifespan` | `Lifespan \| None` | `None` | Async context manager for startup/shutdown |
| `dependencies` | `Sequence[Depends] \| None` | `None` | Global dependencies applied to all routes |
| `middleware` | `Sequence[Middleware] \| None` | `None` | Middleware stack |
| `exception_handlers` | `dict \| None` | `None` | Custom exception handlers |
| `responses` | `dict \| None` | `None` | Additional OpenAPI responses for all endpoints |
| `callbacks` | `list \| None` | `None` | OpenAPI callbacks |
| `webhooks` | `APIRouter \| None` | `None` | OpenAPI webhooks |
| `terms_of_service` | `str \| None` | `None` | URL to terms of service |
| `contact` | `dict \| None` | `None` | Contact info for the API |
| `license_info` | `dict \| None` | `None` | License info for the API |
| `openapi_tags` | `list[dict] \| None` | `None` | Tag metadata for OpenAPI docs |
| `servers` | `list[dict] \| None` | `None` | Server list for OpenAPI schema |
| `swagger_ui_parameters` | `dict \| None` | `None` | Custom Swagger UI config parameters |
| `swagger_ui_oauth2_redirect_url` | `str \| None` | `"/docs/oauth2-redirect"` | OAuth2 redirect URL for Swagger UI |
| `swagger_ui_init_oauth` | `dict \| None` | `None` | OAuth2 init config for Swagger UI |
| `generate_unique_id_function` | `Callable` | auto | Function to generate operation IDs |
| `separate_input_output_schemas` | `bool` | `True` | Generate separate schemas for input/output models |
| `strict_content_type` | `bool` | `True` | Enforce Content-Type header validation (CSRF protection) |
| `include_in_schema` | `bool` | `True` | Include this app in the OpenAPI schema |
| `deprecated` | `bool \| None` | `None` | Mark all routes as deprecated in OpenAPI |
| `openapi_external_docs` | `dict \| None` | `None` | External documentation link for OpenAPI |

### Practical Setup Examples

**Production-ready configuration:**

```python
from fastapi import FastAPI

app = FastAPI(
    title="My Service API",
    version="2.1.0",
    description="Service for managing inventory and orders.",
    docs_url="/api/docs",
    redoc_url="/api/redoc",
    openapi_url="/api/openapi.json",
    root_path="/v2",
)
```

**Disable all documentation endpoints (headless API):**

```python
app = FastAPI(
    openapi_url=None,  # disables /openapi.json, /docs, and /redoc
)
```

> [!NOTE]
> Setting `openapi_url=None` disables the OpenAPI schema entirely, which also disables both Swagger UI and ReDoc since they depend on the schema. To disable only one docs interface, set `docs_url=None` or `redoc_url=None` individually.

**Lifespan events (startup/shutdown):**

```python
from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: initialize resources
    db_pool = await create_db_pool()
    app.state.db = db_pool
    yield
    # Shutdown: clean up resources
    await db_pool.close()

app = FastAPI(lifespan=lifespan)
```

> [!WARNING]
> The `on_startup` and `on_shutdown` event handlers are deprecated. Use the `lifespan` context manager instead. The lifespan approach properly handles resource sharing between startup and shutdown, and ensures cleanup runs even if startup fails partway through.

## 4. Core Usage

### 4.1 Defining Path Operations

Every endpoint in FastAPI is a "path operation" consisting of an HTTP method decorator and a function:

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/items")
async def list_items():
    return [{"id": 1, "name": "Widget"}]

@app.post("/items", status_code=201)
async def create_item(name: str):
    return {"id": 2, "name": name}

@app.put("/items/{item_id}")
async def update_item(item_id: int, name: str):
    return {"id": item_id, "name": name}

@app.patch("/items/{item_id}")
async def partial_update(item_id: int, name: str | None = None):
    return {"id": item_id, "name": name}

@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
    return None
```

**All available HTTP method decorators:**

| Decorator | HTTP Method | Typical Use |
|-----------|------------|-------------|
| `@app.get()` | GET | Retrieve resources |
| `@app.post()` | POST | Create resources |
| `@app.put()` | PUT | Full resource replacement |
| `@app.patch()` | PATCH | Partial updates |
| `@app.delete()` | DELETE | Remove resources |
| `@app.options()` | OPTIONS | CORS preflight, capability discovery |
| `@app.head()` | HEAD | Retrieve headers only (no body) |
| `@app.trace()` | TRACE | Diagnostic echo |
| `@app.api_route()` | Any/multiple | Register one function for multiple methods |

**Using `api_route` for multiple methods:**

```python
@app.api_route("/health", methods=["GET", "HEAD"])
async def health():
    return {"status": "ok"}
```

### 4.2 Path Parameters

Path parameters are extracted from the URL path and automatically converted to the declared Python type:

```python
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"user_id": user_id}
```

Requesting `/users/42` returns `{"user_id": 42}`. Requesting `/users/abc` returns a 422 validation error automatically.

**Path parameter validation with `Path()`:**

```python
from fastapi import Path

@app.get("/items/{item_id}")
async def get_item(
    item_id: int = Path(title="Item ID", ge=1, le=10000)
):
    return {"item_id": item_id}
```

**Predefined values with Enum:**

```python
from enum import Enum

class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    return {"model": model_name.value}
```

**File-path parameters (capturing slashes):**

```python
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"path": file_path}
```

Requesting `/files/home/user/data.csv` captures `file_path = "home/user/data.csv"`.

### 4.3 Query Parameters

Any function parameter that is not a path parameter is automatically treated as a query parameter:

```python
@app.get("/items")
async def list_items(skip: int = 0, limit: int = 10, q: str | None = None):
    return {"skip": skip, "limit": limit, "q": q}
```

`GET /items?skip=20&limit=5&q=widget` parses all three parameters with type validation.

**Query parameter validation with `Query()`:**

```python
from fastapi import Query

@app.get("/search")
async def search(
    q: str = Query(min_length=3, max_length=50, pattern="^[a-zA-Z0-9 ]+$"),
    page: int = Query(default=1, ge=1),
    size: int = Query(default=20, le=100),
):
    return {"q": q, "page": page, "size": size}
```

**Receiving a list of query parameters:**

```python
@app.get("/items")
async def filter_items(
    tags: list[str] = Query(default=[])
):
    return {"tags": tags}
```

`GET /items?tags=python&tags=fastapi` produces `tags = ["python", "fastapi"]`.

**Required query parameters (no default value):**

```python
@app.get("/items")
async def list_items(category: str):  # required, no default
    return {"category": category}
```

### 4.4 Request Body with Pydantic Models

Define request and response schemas with Pydantic models. FastAPI validates incoming JSON automatically:

```python
from pydantic import BaseModel, Field

class ItemCreate(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    price: float = Field(gt=0)
    description: str | None = None
    tags: list[str] = []

class ItemResponse(BaseModel):
    id: int
    name: str
    price: float
    description: str | None
    tags: list[str]

@app.post("/items", response_model=ItemResponse, status_code=201)
async def create_item(item: ItemCreate):
    # item is already validated
    return ItemResponse(id=1, **item.model_dump())
```

**Sending the request:**

```bash
curl -X POST http://127.0.0.1:8000/items \
  -H "Content-Type: application/json" \
  -d '{"name": "Widget", "price": 9.99, "tags": ["sale"]}'
```

```json
{"id": 1, "name": "Widget", "price": 9.99, "description": null, "tags": ["sale"]}
```

**Multiple body parameters:**

```python
from fastapi import Body

@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: ItemCreate,
    importance: int = Body(gt=0),
):
    return {"item_id": item_id, "item": item, "importance": importance}
```

Expected body: `{"item": {...}, "importance": 5}`.

**Embedding a single body parameter:**

```python
@app.post("/items")
async def create_item(item: ItemCreate = Body(embed=True)):
    return item
```

Expected body: `{"item": {...}}` instead of just `{...}`.

### 4.5 Headers and Cookies

```python
from fastapi import Header, Cookie

@app.get("/info")
async def info(
    user_agent: str | None = Header(default=None),
    accept_language: str | None = Header(default=None),
    session_id: str | None = Cookie(default=None),
):
    return {
        "user_agent": user_agent,
        "accept_language": accept_language,
        "session_id": session_id,
    }
```

> [!NOTE]
> HTTP headers use hyphens (`User-Agent`), but Python variables cannot. FastAPI automatically converts underscores to hyphens, so `user_agent` maps to the `User-Agent` header. You can disable this with `Header(convert_underscores=False)`.

**Duplicate headers (e.g., multiple `X-Token` values):**

```python
@app.get("/items")
async def read_items(x_token: list[str] | None = Header(default=None)):
    return {"X-Token values": x_token}
```

### 4.6 Response Handling

**Setting response status codes:**

```python
from fastapi import status

@app.post("/items", status_code=status.HTTP_201_CREATED)
async def create_item(item: ItemCreate):
    return item
```

**Custom response headers and cookies:**

```python
from fastapi import Response

@app.get("/custom")
async def custom_response(response: Response):
    response.headers["X-Custom-Header"] = "custom-value"
    response.set_cookie(key="session", value="abc123", httponly=True)
    return {"message": "done"}
```

**Available response classes:**

| Class | Content-Type | Import |
|-------|-------------|--------|
| `JSONResponse` | `application/json` | `fastapi.responses` |
| `HTMLResponse` | `text/html` | `fastapi.responses` |
| `PlainTextResponse` | `text/plain` | `fastapi.responses` |
| `RedirectResponse` | (redirect) | `fastapi.responses` |
| `StreamingResponse` | any | `fastapi.responses` |
| `FileResponse` | auto-detected | `fastapi.responses` |
| `ORJSONResponse` | `application/json` | `fastapi.responses` (requires `orjson`) |
| `UJSONResponse` | `application/json` | `fastapi.responses` (requires `ujson`) |

**Returning a file:**

```python
from fastapi.responses import FileResponse

@app.get("/download")
async def download():
    return FileResponse(
        path="report.pdf",
        filename="report.pdf",
        media_type="application/pdf",
    )
```

**Streaming response:**

```python
from fastapi.responses import StreamingResponse

@app.get("/stream")
async def stream():
    async def generate():
        for i in range(100):
            yield f"data: chunk {i}\n\n"
    return StreamingResponse(generate(), media_type="text/event-stream")
```

**Controlling response model output:**

```python
@app.get("/items/{item_id}", response_model=ItemResponse)
async def get_item(item_id: int):
    return {"id": item_id, "name": "Widget", "price": 9.99,
            "internal_code": "W001"}  # internal_code is filtered OUT
```

`response_model` filters the output to include only fields defined in `ItemResponse`. Use `response_model_exclude_unset=True` to omit fields the user did not set.

### 4.7 Form Data and File Uploads

**Receiving form fields:**

```python
from fastapi import Form

@app.post("/login")
async def login(username: str = Form(), password: str = Form()):
    return {"username": username}
```

> [!IMPORTANT]
> Form data and file uploads require the `python-multipart` package. This is included in `fastapi[standard]` but must be installed separately with the minimal install: `pip install python-multipart`.

**Single file upload:**

```python
from fastapi import File, UploadFile

@app.post("/upload")
async def upload(file: UploadFile):
    contents = await file.read()
    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "size": len(contents),
    }
```

**Multiple file uploads:**

```python
@app.post("/upload-many")
async def upload_many(files: list[UploadFile]):
    return {"filenames": [f.filename for f in files]}
```

**File with form fields together:**

```python
@app.post("/submit")
async def submit(
    title: str = Form(),
    description: str = Form(default=""),
    file: UploadFile = File(),
):
    return {"title": title, "filename": file.filename}
```

**`UploadFile` attributes and methods:**

| Attribute/Method | Description |
|-----------------|-------------|
| `filename` | Original filename from the client |
| `content_type` | MIME type (e.g., `image/png`) |
| `size` | File size in bytes (if available) |
| `headers` | Raw headers from the multipart section |
| `read(size)` | Read bytes (async) |
| `write(data)` | Write bytes (async) |
| `seek(offset)` | Seek to position (async) |
| `close()` | Close the file (async) |

### 4.8 Dependency Injection

FastAPI's dependency injection system lets you declare reusable logic that runs before your endpoint:

```python
from fastapi import Depends

async def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users")
async def list_users(db: Session = Depends(get_db)):
    return db.query(User).all()
```

**Class-based dependencies:**

```python
class Pagination:
    def __init__(self, skip: int = 0, limit: int = 100):
        self.skip = skip
        self.limit = limit

@app.get("/items")
async def list_items(pagination: Pagination = Depends()):
    return {"skip": pagination.skip, "limit": pagination.limit}
```

**Nested dependencies:**

```python
async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = decode_token(token)
    return user

async def get_active_user(user: User = Depends(get_current_user)):
    if not user.is_active:
        raise HTTPException(status_code=400, detail="Inactive user")
    return user

@app.get("/me")
async def read_me(user: User = Depends(get_active_user)):
    return user
```

**Global dependencies (applied to all routes):**

```python
app = FastAPI(dependencies=[Depends(verify_api_key)])
```

**Router-level dependencies:**

```python
router = APIRouter(dependencies=[Depends(verify_admin)])

@router.get("/admin/stats")
async def admin_stats():
    return {"users": 1000}
```

**Dependencies with `yield` (resource cleanup):**

Dependencies using `yield` execute code before AND after the endpoint. The code after `yield` runs during cleanup, even if the endpoint raises an exception:

```python
async def get_db_session():
    session = create_session()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()
```

### 4.9 Error Handling

**Raising HTTP exceptions:**

```python
from fastapi import HTTPException

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(
            status_code=404,
            detail="Item not found",
            headers={"X-Error": "Item lookup failed"},
        )
    return items_db[item_id]
```

**Custom exception handlers:**

```python
from fastapi import Request
from fastapi.responses import JSONResponse

class ItemNotFoundError(Exception):
    def __init__(self, item_id: int):
        self.item_id = item_id

@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request: Request, exc: ItemNotFoundError):
    return JSONResponse(
        status_code=404,
        content={"detail": f"Item {exc.item_id} does not exist"},
    )

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id not in items_db:
        raise ItemNotFoundError(item_id)
    return items_db[item_id]
```

**Overriding default validation error responses:**

```python
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse

@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
    return PlainTextResponse(str(exc), status_code=422)
```

**Overriding the default HTTPException handler:**

```python
from starlette.exceptions import HTTPException as StarletteHTTPException

@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
```

### 4.10 Middleware

Middleware runs code before every request and after every response:

```python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
```

**Built-in middleware options:**

| Middleware | Purpose | Key Parameters |
|-----------|---------|---------------|
| `CORSMiddleware` | Cross-Origin Resource Sharing | `allow_origins`, `allow_methods`, `allow_headers`, `allow_credentials` |
| `GZipMiddleware` | Compress responses | `minimum_size` (default 500 bytes) |
| `HTTPSRedirectMiddleware` | Redirect HTTP to HTTPS | (none) |
| `TrustedHostMiddleware` | Validate Host header | `allowed_hosts` |

**Custom middleware (function-based):**

```python
import time

@app.middleware("http")
async def add_timing_header(request: Request, call_next):
    start = time.perf_counter()
    response = await call_next(request)
    elapsed = time.perf_counter() - start
    response.headers["X-Process-Time"] = str(elapsed)
    return response
```

### 4.11 Security and Authentication

FastAPI provides security utilities that integrate with the OpenAPI schema so the interactive docs show authentication controls.

**OAuth2 with Password Bearer:**

```python
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")

@app.post("/token")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    user = authenticate(form.username, form.password)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    token = create_access_token(user)
    return {"access_token": token, "token_type": "bearer"}

@app.get("/users/me")
async def read_me(token: str = Depends(oauth2_scheme)):
    user = decode_token(token)
    return user
```

**API Key authentication:**

```python
from fastapi.security import APIKeyHeader

api_key_header = APIKeyHeader(name="X-API-Key")

@app.get("/protected")
async def protected(api_key: str = Depends(api_key_header)):
    if api_key != "secret-key":
        raise HTTPException(status_code=403, detail="Invalid API key")
    return {"data": "protected"}
```

**All available security schemes:**

| Class | Location | OpenAPI Type |
|-------|----------|-------------|
| `OAuth2PasswordBearer` | `Authorization: Bearer <token>` | OAuth2 |
| `OAuth2AuthorizationCodeBearer` | `Authorization: Bearer <token>` | OAuth2 (auth code flow) |
| `HTTPBasic` | `Authorization: Basic <b64>` | HTTP |
| `HTTPBearer` | `Authorization: Bearer <token>` | HTTP |
| `HTTPDigest` | `Authorization: Digest <params>` | HTTP |
| `APIKeyQuery` | Query parameter | API Key |
| `APIKeyHeader` | Request header | API Key |
| `APIKeyCookie` | Cookie | API Key |
| `OpenIdConnect` | OpenID Connect discovery | OpenID Connect |

**Using `Security()` with scopes:**

```python
from fastapi import Security
from fastapi.security import OAuth2PasswordBearer, SecurityScopes

oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="/token",
    scopes={"items:read": "Read items", "items:write": "Write items"},
)

async def get_current_user(
    security_scopes: SecurityScopes,
    token: str = Depends(oauth2_scheme),
):
    # Verify token has required scopes
    ...

@app.get("/items")
async def read_items(
    user: User = Security(get_current_user, scopes=["items:read"])
):
    return []
```

### 4.12 Background Tasks

Execute tasks after returning the response to the client:

```python
from fastapi import BackgroundTasks

def send_notification(email: str, message: str):
    # This runs in the background after response is sent
    send_email(email, message)

@app.post("/orders")
async def create_order(order: Order, background_tasks: BackgroundTasks):
    result = process_order(order)
    background_tasks.add_task(send_notification, order.email, "Order received")
    return result
```

**Multiple background tasks run sequentially in the order they were added.** They execute in the same process, so they share memory with the app.

> [!CAUTION]
> Background tasks are not suitable for heavy, long-running computations. They run in the same event loop (for async tasks) or thread pool (for sync tasks). For heavy work, use a proper task queue like Celery, Dramatiq, or ARQ.

### 4.13 WebSockets

```python
from fastapi import WebSocket, WebSocketDisconnect

@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
    await ws.accept()
    try:
        while True:
            data = await ws.receive_text()
            await ws.send_text(f"Echo: {data}")
    except WebSocketDisconnect:
        print("Client disconnected")
```

**WebSocket with path and query parameters:**

```python
@app.websocket("/ws/{room_id}")
async def chat(ws: WebSocket, room_id: int, token: str | None = None):
    if not verify_token(token):
        await ws.close(code=1008)
        return
    await ws.accept()
    ...
```

**WebSocket with dependencies:**

```python
@app.websocket("/ws")
async def ws_endpoint(ws: WebSocket, db: Session = Depends(get_db)):
    await ws.accept()
    ...
```

**`WebSocket` methods:**

| Method | Direction | Description |
|--------|-----------|-------------|
| `accept()` | Server | Accept the connection |
| `close(code)` | Server | Close the connection |
| `send_text(data)` | Server -> Client | Send text message |
| `send_bytes(data)` | Server -> Client | Send binary message |
| `send_json(data)` | Server -> Client | Send JSON message |
| `receive_text()` | Client -> Server | Receive text message |
| `receive_bytes()` | Client -> Server | Receive binary message |
| `receive_json()` | Client -> Server | Receive JSON message |

### 4.14 Server-Sent Events (SSE)

FastAPI supports SSE through `EventSourceResponse`:

```python
from fastapi.responses import EventSourceResponse, ServerSentEvent
import asyncio

@app.get("/events")
async def events():
    async def event_generator():
        for i in range(10):
            yield ServerSentEvent(
                data=f"Message {i}",
                event="update",
                id=str(i),
            )
            await asyncio.sleep(1)

    return EventSourceResponse(event_generator())
```

### 4.15 Modular Applications with APIRouter

Organize large applications into separate files using `APIRouter`:

```python
# routers/users.py
from fastapi import APIRouter

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
async def list_users():
    return []

@router.get("/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id}
```

```python
# main.py
from fastapi import FastAPI
from routers import users, items

app = FastAPI()
app.include_router(users.router)
app.include_router(items.router, prefix="/api/v1")
```

**APIRouter supports all the same parameters as FastAPI for organizing endpoints:**

| Parameter | Purpose |
|-----------|---------|
| `prefix` | URL prefix for all routes in this router |
| `tags` | OpenAPI tags for documentation grouping |
| `dependencies` | Dependencies applied to all routes in this router |
| `responses` | Additional OpenAPI responses |
| `default_response_class` | Default response class for routes |

### 4.16 Sub-Applications

Mount independent FastAPI applications under a path:

```python
main_app = FastAPI()
admin_app = FastAPI()

@admin_app.get("/dashboard")
async def dashboard():
    return {"admin": True}

main_app.mount("/admin", admin_app)
```

The mounted app at `/admin` has its own independent OpenAPI docs at `/admin/docs`.

### 4.17 Static Files

```python
from fastapi.staticfiles import StaticFiles

app.mount("/static", StaticFiles(directory="static"), name="static")
```

### 4.18 Templates

```python
from fastapi import Request
from fastapi.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")

@app.get("/page", response_class=HTMLResponse)
async def page(request: Request):
    return templates.TemplateResponse(
        request=request,
        name="page.html",
        context={"title": "My Page"},
    )
```

### 4.19 Dataclass Support

FastAPI supports Python dataclasses as request and response models:

```python
from dataclasses import dataclass, field

@dataclass
class Item:
    name: str
    price: float
    tags: list[str] = field(default_factory=list)

@app.post("/items")
async def create_item(item: Item):
    return item
```

### 4.20 OpenAPI Customization

**Tag metadata for documentation grouping:**

```python
tags_metadata = [
    {"name": "users", "description": "Operations with users"},
    {"name": "items", "description": "Manage items", "externalDocs": {
        "description": "Items external docs",
        "url": "https://example.com/items",
    }},
]

app = FastAPI(openapi_tags=tags_metadata)

@app.get("/users", tags=["users"])
async def get_users():
    return []
```

**Webhooks:**

```python
webhooks = APIRouter()

@webhooks.post("new-subscription")
async def new_subscription(body: SubscriptionPayload):
    """Called when a new subscription is created."""
    ...

app = FastAPI(webhooks=webhooks)
```

Webhooks appear in the OpenAPI schema for client code generation but are not routable endpoints in your app.

**Custom OpenAPI schema:**

```python
from fastapi.openapi.utils import get_openapi

def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    schema = get_openapi(
        title=app.title,
        version=app.version,
        routes=app.routes,
    )
    schema["info"]["x-logo"] = {"url": "https://example.com/logo.png"}
    app.openapi_schema = schema
    return schema

app.openapi = custom_openapi
```

## 5. Common Tasks

### 5.1 CRUD API Pattern

A complete CRUD implementation with proper status codes and models:

```python
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel

app = FastAPI()

class ItemCreate(BaseModel):
    name: str
    price: float

class ItemUpdate(BaseModel):
    name: str | None = None
    price: float | None = None

class ItemOut(BaseModel):
    id: int
    name: str
    price: float

db: dict[int, dict] = {}
counter = 0

@app.post("/items", response_model=ItemOut, status_code=status.HTTP_201_CREATED)
async def create(item: ItemCreate):
    global counter
    counter += 1
    record = {"id": counter, **item.model_dump()}
    db[counter] = record
    return record

@app.get("/items", response_model=list[ItemOut])
async def list_all(skip: int = 0, limit: int = 20):
    return list(db.values())[skip : skip + limit]

@app.get("/items/{item_id}", response_model=ItemOut)
async def get_one(item_id: int):
    if item_id not in db:
        raise HTTPException(404, "Item not found")
    return db[item_id]

@app.patch("/items/{item_id}", response_model=ItemOut)
async def update(item_id: int, updates: ItemUpdate):
    if item_id not in db:
        raise HTTPException(404, "Item not found")
    stored = db[item_id]
    for key, val in updates.model_dump(exclude_unset=True).items():
        stored[key] = val
    return stored

@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete(item_id: int):
    if item_id not in db:
        raise HTTPException(404, "Item not found")
    del db[item_id]
```

### 5.2 Database Integration (SQLAlchemy)

```python
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session, DeclarativeBase
from fastapi import Depends

engine = create_engine("sqlite:///./app.db", connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine)

class Base(DeclarativeBase):
    pass

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users")
def list_users(db: Session = Depends(get_db)):
    return db.query(UserModel).all()
```

### 5.3 Database Integration (SQLModel)

SQLModel combines SQLAlchemy and Pydantic in a single model:

```python
from sqlmodel import SQLModel, Field, Session, create_engine, select

class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None

engine = create_engine("sqlite:///heroes.db")
SQLModel.metadata.create_all(engine)

def get_session():
    with Session(engine) as session:
        yield session

@app.post("/heroes", response_model=Hero)
def create_hero(hero: Hero, session: Session = Depends(get_session)):
    session.add(hero)
    session.commit()
    session.refresh(hero)
    return hero

@app.get("/heroes", response_model=list[Hero])
def list_heroes(session: Session = Depends(get_session)):
    return session.exec(select(Hero)).all()
```

### 5.4 JWT Authentication

```python
from datetime import datetime, timedelta
from jose import jwt, JWTError
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")

def create_access_token(data: dict):
    to_encode = data.copy()
    to_encode["exp"] = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

async def get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="Invalid token")
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")
    user = get_user_from_db(username)
    if user is None:
        raise HTTPException(status_code=401, detail="User not found")
    return user

@app.get("/protected")
async def protected_route(user: User = Depends(get_current_user)):
    return {"user": user.username}
```

### 5.5 CORS for Frontend Integration

```python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:3000",
        "https://myapp.example.com",
    ],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
    expose_headers=["X-Total-Count"],
    max_age=600,
)
```

### 5.6 Rate Limiting Pattern

FastAPI does not include built-in rate limiting, but you can implement it with middleware or dependencies:

```python
from collections import defaultdict
import time
from fastapi import Request, HTTPException

request_counts: dict[str, list[float]] = defaultdict(list)

async def rate_limit(request: Request):
    client_ip = request.client.host
    now = time.time()
    window = 60  # seconds
    max_requests = 100

    # Remove old entries
    request_counts[client_ip] = [
        t for t in request_counts[client_ip] if now - t < window
    ]

    if len(request_counts[client_ip]) >= max_requests:
        raise HTTPException(status_code=429, detail="Rate limit exceeded")

    request_counts[client_ip].append(now)

app = FastAPI(dependencies=[Depends(rate_limit)])
```

### 5.7 Testing FastAPI Applications

FastAPI provides a `TestClient` built on `httpx`:

```python
from fastapi.testclient import TestClient

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}

def test_create_item():
    response = client.post("/items", json={"name": "Widget", "price": 9.99})
    assert response.status_code == 201
    assert response.json()["name"] == "Widget"

def test_validation_error():
    response = client.post("/items", json={"name": "", "price": -1})
    assert response.status_code == 422

def test_not_found():
    response = client.get("/items/99999")
    assert response.status_code == 404
```

**Async testing with `pytest-asyncio` and `httpx`:**

```python
import pytest
from httpx import AsyncClient, ASGITransport

@pytest.mark.anyio
async def test_async():
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        response = await client.get("/")
        assert response.status_code == 200
```

**Overriding dependencies in tests:**

```python
def get_test_db():
    return test_database

app.dependency_overrides[get_db] = get_test_db

client = TestClient(app)
response = client.get("/users")

# Clean up
app.dependency_overrides.clear()
```

### 5.8 Pagination Pattern

```python
from pydantic import BaseModel
from fastapi import Query

class PaginatedResponse(BaseModel):
    items: list
    total: int
    page: int
    size: int
    pages: int

@app.get("/items", response_model=PaginatedResponse)
async def list_items(
    page: int = Query(default=1, ge=1),
    size: int = Query(default=20, ge=1, le=100),
):
    total = len(all_items)
    start = (page - 1) * size
    items = all_items[start : start + size]
    return PaginatedResponse(
        items=items,
        total=total,
        page=page,
        size=size,
        pages=(total + size - 1) // size,
    )
```

### 5.9 Request Validation Patterns

**Nested models:**

```python
class Address(BaseModel):
    street: str
    city: str
    zip_code: str = Field(pattern=r"^\d{5}(-\d{4})?$")

class UserCreate(BaseModel):
    name: str
    email: str
    addresses: list[Address] = []
```

**Custom validators:**

```python
from pydantic import field_validator

class Order(BaseModel):
    quantity: int
    unit_price: float

    @field_validator("quantity")
    @classmethod
    def quantity_must_be_positive(cls, v):
        if v <= 0:
            raise ValueError("Quantity must be positive")
        return v
```

**Model with examples for OpenAPI docs:**

```python
class Item(BaseModel):
    name: str
    price: float

    model_config = {
        "json_schema_extra": {
            "examples": [
                {"name": "Widget", "price": 9.99},
                {"name": "Gadget", "price": 24.99},
            ]
        }
    }
```

### 5.10 Deploying Behind a Reverse Proxy

When deploying behind Nginx, Traefik, or similar:

```python
app = FastAPI(root_path="/api/v1")
```

Or pass it at the ASGI server level:

```bash
uvicorn main:app --root-path /api/v1
```

This ensures the OpenAPI schema and documentation URLs correctly reflect the proxy prefix.

### 5.11 Multi-Worker Production Deployment

**Using Gunicorn with Uvicorn workers:**

```bash
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
```

**Docker deployment:**

```dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

CMD ["fastapi", "run", "main.py", "--port", "8000"]
```

> [!TIP]
> For multi-worker deployments, avoid storing state in module-level variables (they are not shared between workers). Use a database, Redis, or similar shared store.

### 5.12 Structured Logging

```python
import logging
from fastapi import Request

logger = logging.getLogger("myapp")

@app.middleware("http")
async def log_requests(request: Request, call_next):
    logger.info(f"{request.method} {request.url.path}")
    response = await call_next(request)
    logger.info(f"Response: {response.status_code}")
    return response
```

### 5.13 Health Check Endpoint

```python
@app.get("/health")
async def health():
    return {"status": "healthy"}

@app.get("/ready")
async def readiness(db: Session = Depends(get_db)):
    try:
        db.execute(text("SELECT 1"))
        return {"status": "ready"}
    except Exception:
        raise HTTPException(503, "Database unavailable")
```

## 6. Troubleshooting

### Installation Issues

| # | Problem | Solution |
|---|---------|----------|
| 1 | `RuntimeError: To use the fastapi command, please install "fastapi[standard]"` | Run `pip install "fastapi[standard]"` instead of `pip install fastapi` |
| 2 | `ModuleNotFoundError: No module named 'pydantic'` | FastAPI requires Pydantic; reinstall with `pip install fastapi` to pull dependencies |
| 3 | `pip install fastapi[standard]` fails in zsh | Quote the brackets: `pip install "fastapi[standard]"` (zsh interprets brackets as glob) |
| 4 | Conflicting Pydantic versions | FastAPI requires Pydantic v2; run `pip install "pydantic>=2.0"` to upgrade |
| 5 | `ImportError: cannot import name 'PydanticV1NotSupportedError'` | You are using Pydantic v1 which is no longer supported; upgrade to Pydantic v2 |

### Server and Runtime Issues

| # | Problem | Solution |
|---|---------|----------|
| 6 | `Address already in use` when starting | Another process is using port 8000; kill it or use `--port 8001` |
| 7 | Server starts but no auto-reload | Use `fastapi dev` (not `fastapi run`) or add `--reload` flag to uvicorn |
| 8 | `asyncio.run() cannot be called from a running event loop` | You are calling sync code inside an async endpoint; use `await` or run in executor |
| 9 | Slow response with sync `def` endpoints | Sync endpoints run in a thread pool (limited threads); switch to `async def` for I/O-bound work, or increase thread pool size |
| 10 | `uvicorn` not found after install | Ensure your virtual environment is activated; `pip install uvicorn[standard]` |

### Request Handling Issues

| # | Problem | Solution |
|---|---------|----------|
| 11 | `422 Unprocessable Entity` on POST requests | Request body does not match the Pydantic model; check the `detail` array in the response for specific field errors |
| 12 | `422` error when sending form data | Ensure you are using `Form()` parameters, not a Pydantic body model. Also install `python-multipart` |
| 13 | Query parameters not received | Parameters with default values of `None` are optional; required params have no default. Check parameter names match the URL query string |
| 14 | `405 Method Not Allowed` | You are using the wrong HTTP method (e.g., GET instead of POST). Check the decorator |
| 15 | Path parameters received as strings despite `int` annotation | This should not happen with FastAPI; verify the path parameter syntax is `{param_name}` and the annotation is correct |
| 16 | `307 Temporary Redirect` on POST | FastAPI's `redirect_slashes=True` (default) redirects `/items` to `/items/`; on redirect, POST becomes GET. Define your route with a trailing slash or set `redirect_slashes=False` |
| 17 | Request body is empty/None | Ensure `Content-Type: application/json` header is set. With `strict_content_type=True` (default), missing or wrong Content-Type causes rejection |
| 18 | File uploads fail silently | Verify `python-multipart` is installed and the parameter uses `UploadFile` or `File()` |
| 19 | Large file upload times out | Configure your ASGI server timeout; Uvicorn default is no timeout but reverse proxies may have limits |
| 20 | Duplicate query parameter values lost | Use `list[str]` type annotation with `Query()` to receive all values |

### CORS and Security Issues

| # | Problem | Solution |
|---|---------|----------|
| 21 | CORS errors in browser | Add `CORSMiddleware` with the correct `allow_origins`. Wildcard `["*"]` works for dev but specify exact origins for production |
| 22 | CORS preflight fails despite middleware | Ensure `allow_methods` includes the method being used and `allow_headers` includes custom headers. If using credentials, `allow_origins` cannot be `["*"]` |
| 23 | OAuth2 "Not authenticated" in Swagger UI | Click the "Authorize" button in Swagger UI and enter your token before making requests |
| 24 | Cookies not sent in cross-origin requests | Set `allow_credentials=True` in CORS middleware and use `withCredentials: true` on the client side |

### Response and Serialization Issues

| # | Problem | Solution |
|---|---------|----------|
| 25 | `datetime` objects serialized as strings unexpectedly | This is correct behavior; JSON does not have a datetime type. Pydantic serializes to ISO 8601 strings |
| 26 | Response includes extra fields not in `response_model` | This should not happen; `response_model` filters output. Verify you are using `response_model=` in the decorator, not just the return type hint |
| 27 | `response_model` causes performance issues | For large responses, use `response_model_exclude_unset=True` to skip unnecessary validation. Or return a `Response` directly |
| 28 | Binary data in JSON response | Return binary content using `StreamingResponse` or `FileResponse` instead of `JSONResponse` |
| 29 | `ORJSONResponse` raises `ImportError` | Install `orjson`: `pip install orjson` |
| 30 | Nested model fields show as `null` despite being set | Check that nested Pydantic models use proper type annotations; `Optional[Model]` requires explicit `None` default |

### Testing Issues

| # | Problem | Solution |
|---|---------|----------|
| 31 | `TestClient` not found | Install `httpx`: `pip install httpx` (included with `fastapi[standard]`) |
| 32 | Tests fail with dependency injection | Use `app.dependency_overrides[dependency_func] = mock_func` to replace dependencies in tests |
| 33 | Async tests fail | Use `pytest-anyio` and `httpx.AsyncClient` with `ASGITransport` for async test support |
| 34 | WebSocket tests hang | Use `with client.websocket_connect("/ws") as ws:` context manager pattern |
| 35 | File upload tests fail | Pass files as `files={"file": ("name.txt", b"content", "text/plain")}` in the `TestClient` call |

### Deployment Issues

| # | Problem | Solution |
|---|---------|----------|
| 36 | OpenAPI docs show wrong URLs behind reverse proxy | Set `root_path` in `FastAPI()` constructor or pass `--root-path` to uvicorn |
| 37 | Static files return 404 in production | Ensure the `directory` path in `StaticFiles()` is absolute or correctly relative to the working directory |
| 38 | Gunicorn workers crash with `[CRITICAL] WORKER TIMEOUT` | Increase `--timeout` (default 30s) or investigate if an endpoint is blocking too long |
| 39 | Memory grows unbounded with streaming | Ensure you are using `yield` in streaming responses and not accumulating data in memory |
| 40 | Application does not shut down cleanly | Use the `lifespan` context manager to properly close database connections, HTTP clients, and other resources |

## 7. FAQ

**Q1: What Python versions does FastAPI support?**
FastAPI requires Python 3.8 or higher. Type hint features used by FastAPI (like `str | None`) require Python 3.10+; for older Python versions use `Optional[str]` from `typing`.

**Q2: Is FastAPI async or sync?**
Both. Endpoints declared with `async def` run on the async event loop. Endpoints declared with `def` run in an external thread pool automatically. You can mix both freely in the same application.

**Q3: How does FastAPI compare to Flask in performance?**
FastAPI runs on ASGI (Starlette + Uvicorn), which supports async I/O natively. For I/O-bound workloads, FastAPI significantly outperforms Flask (WSGI). For CPU-bound work, both are limited by Python's GIL.

**Q4: Can I use FastAPI without Pydantic models?**
Yes. You can use plain Python types for path, query, header, and cookie parameters. However, for request bodies, Pydantic models are the recommended approach. You can also use dataclasses.

**Q5: How do I disable the docs endpoints in production?**
Set `openapi_url=None` in the `FastAPI()` constructor. This disables `/openapi.json`, `/docs`, and `/redoc` entirely.

**Q6: Can I use SQLAlchemy / SQLModel / Tortoise ORM / MongoDB with FastAPI?**
Yes. FastAPI is ORM-agnostic. Use dependency injection to provide database sessions. SQLModel is particularly well-integrated since it combines SQLAlchemy and Pydantic.

**Q7: How do I handle CORS?**
Add `CORSMiddleware` via `app.add_middleware()`. Specify allowed origins, methods, and headers. See section 5.5 for a complete example.

**Q8: What is `response_model` vs return type annotation?**
`response_model` is a decorator parameter that filters and validates the response. Return type annotations (e.g., `-> ItemOut`) also work for the OpenAPI schema. If both are provided, `response_model` takes priority.

**Q9: How do I version my API?**
Common approaches: (1) URL prefix versioning with `APIRouter(prefix="/v1")` or `app.mount()`, (2) header-based versioning with custom middleware, (3) separate `FastAPI()` instances mounted as sub-applications.

**Q10: Can I serve HTML pages with FastAPI?**
Yes. Use `Jinja2Templates` for server-rendered HTML and `HTMLResponse` as the response class. You can also mount `StaticFiles` for CSS, JS, and images.

**Q11: How do I add request/response logging?**
Use a custom middleware function (see section 5.12). The middleware receives every request and can inspect both the request and response.

**Q12: What is the difference between `fastapi dev` and `fastapi run`?**
`fastapi dev` enables auto-reload and binds to `127.0.0.1` (localhost only). `fastapi run` disables auto-reload and binds to `0.0.0.0` (all interfaces). Use `dev` during development, `run` in production.

**Q13: How do I handle file downloads?**
Return a `FileResponse` with the `path` parameter pointing to the file. Set `filename` for the download name and `media_type` for the Content-Type header.

**Q14: Can I use WebSockets and HTTP endpoints in the same app?**
Yes. WebSocket endpoints use `@app.websocket()` and coexist with HTTP endpoints. They share the same dependency injection system.

**Q15: How do I implement pagination?**
Use query parameters (`page`, `size` or `skip`, `limit`) with `Query()` for validation. Return a response model that includes `total`, `page`, `size`, and `items`. See section 5.8.

**Q16: What is `Depends()` vs `Security()`?**
`Depends()` declares a dependency. `Security()` is identical but additionally accepts OAuth2 `scopes` and integrates with the OpenAPI security scheme display.

**Q17: How do I run background tasks?**
Declare a `BackgroundTasks` parameter in your endpoint and call `.add_task()`. Tasks run after the response is sent. For heavy work, use Celery or a similar task queue.

**Q18: How do I add custom headers to responses?**
Declare a `Response` parameter and set `response.headers["X-Custom"] = "value"`. The response object is merged with the return value.

**Q19: What is `root_path` and when do I need it?**
`root_path` tells FastAPI that it is served under a URL prefix (e.g., `/api/v1`). Set this when deploying behind a reverse proxy that strips the prefix. It ensures OpenAPI docs and redirect URLs are correct.

**Q20: Can I use dependency injection in WebSocket endpoints?**
Yes. WebSocket endpoints support `Depends()` the same way HTTP endpoints do.

**Q21: How do I validate query parameters as enums?**
Define a Python `Enum` class and use it as the type annotation. FastAPI validates that the value is one of the allowed options and generates the enum in OpenAPI.

**Q22: How do I return different status codes from the same endpoint?**
Use `Response` parameter to set `response.status_code` dynamically, or raise `HTTPException` for error cases. Declare additional codes in `responses={}` for OpenAPI documentation.

**Q23: What is `separate_input_output_schemas`?**
When `True` (default), FastAPI generates separate OpenAPI schemas for request bodies and response bodies even if they use the same Pydantic model. This prevents clients from seeing read-only fields in request schemas.

**Q24: What does `strict_content_type=True` do?**
It enforces that the `Content-Type` header matches the expected media type. This provides CSRF protection by rejecting requests that claim to be JSON but are actually form-encoded (which browsers can send without CORS preflight).

**Q25: How do I handle multiple file uploads with metadata?**
Combine `UploadFile` parameters with `Form()` parameters in the same endpoint. Each gets its own parameter declaration.

**Q26: Can I generate client SDKs from my FastAPI app?**
Yes. FastAPI generates a full OpenAPI 3.1.0 schema at `/openapi.json`. Use any OpenAPI code generator (e.g., `openapi-generator`, `swagger-codegen`, `openapi-typescript-codegen`) to generate client libraries.

**Q27: How do I test WebSocket endpoints?**
Use `TestClient` with `client.websocket_connect("/ws")` as a context manager. Inside the context, call `ws.send_text()` and `ws.receive_text()` to simulate communication.

**Q28: How do I handle large request bodies?**
Uvicorn has no built-in body size limit. Add limits at the reverse proxy level (e.g., Nginx `client_max_body_size`) or implement a custom middleware that checks `Content-Length`.

**Q29: What is an APIRouter and when should I use it?**
`APIRouter` is FastAPI's mechanism for organizing endpoints into modules. Use it when your application has multiple resource types or feature areas. Include routers in the main app with `app.include_router()`.

**Q30: How do I use Server-Sent Events (SSE)?**
Return an `EventSourceResponse` that yields `ServerSentEvent` objects. See section 4.14 for a complete example. Clients connect with the EventSource browser API or similar.

## 8. Quick Reference

### Path Operation Decorator Parameters

| Parameter | Type | Purpose |
|-----------|------|---------|
| `path` | `str` | URL path (first positional argument) |
| `response_model` | `type` | Pydantic model for response filtering/validation |
| `status_code` | `int` | Default HTTP status code |
| `tags` | `list[str]` | OpenAPI tags for grouping |
| `summary` | `str` | Short endpoint summary |
| `description` | `str` | Detailed endpoint description |
| `response_description` | `str` | Description of the response |
| `responses` | `dict` | Additional OpenAPI response definitions |
| `deprecated` | `bool` | Mark as deprecated in OpenAPI |
| `operation_id` | `str` | Custom OpenAPI operation ID |
| `include_in_schema` | `bool` | Include in OpenAPI schema |
| `response_model_include` | `set` | Fields to include in response |
| `response_model_exclude` | `set` | Fields to exclude from response |
| `response_model_exclude_unset` | `bool` | Exclude unset fields from response |
| `response_model_exclude_defaults` | `bool` | Exclude default-valued fields |
| `response_model_exclude_none` | `bool` | Exclude None-valued fields |
| `callbacks` | `list` | OpenAPI callback definitions |
| `openapi_extra` | `dict` | Additional OpenAPI metadata |

### Parameter Types

| Function | Source | Example |
|----------|--------|---------|
| `Path()` | URL path segment | `/items/{item_id}` |
| `Query()` | URL query string | `?skip=0&limit=10` |
| `Header()` | HTTP headers | `Authorization: Bearer token` |
| `Cookie()` | HTTP cookies | `session_id=abc123` |
| `Body()` | Request body (JSON) | `{"name": "Widget"}` |
| `Form()` | Form-encoded body | `username=admin&password=secret` |
| `File()` | Multipart file upload | Binary file content |

### Common Validation Parameters

These parameters work with `Path()`, `Query()`, `Body()`, `Header()`, `Cookie()`, `Form()`:

| Parameter | Applies To | Purpose |
|-----------|-----------|---------|
| `default` | All | Default value |
| `title` | All | Schema title for docs |
| `description` | All | Schema description for docs |
| `alias` | All | Alternative field name in the request |
| `min_length` | `str` | Minimum string length |
| `max_length` | `str` | Maximum string length |
| `pattern` | `str` | Regex pattern |
| `ge` | numeric | Greater than or equal |
| `gt` | numeric | Greater than |
| `le` | numeric | Less than or equal |
| `lt` | numeric | Less than |
| `multiple_of` | numeric | Must be a multiple of this value |
| `examples` | All | Example values for OpenAPI docs |
| `deprecated` | All | Mark as deprecated |
| `include_in_schema` | All | Include in OpenAPI schema |

### HTTP Status Code Constants

FastAPI re-exports Starlette's status codes via `fastapi.status`:

| Constant | Code | Typical Use |
|----------|------|-------------|
| `HTTP_200_OK` | 200 | Successful GET/PUT/PATCH |
| `HTTP_201_CREATED` | 201 | Successful POST (resource created) |
| `HTTP_204_NO_CONTENT` | 204 | Successful DELETE (no body) |
| `HTTP_301_MOVED_PERMANENTLY` | 301 | Permanent redirect |
| `HTTP_302_FOUND` | 302 | Temporary redirect |
| `HTTP_304_NOT_MODIFIED` | 304 | Cache hit |
| `HTTP_400_BAD_REQUEST` | 400 | Client error |
| `HTTP_401_UNAUTHORIZED` | 401 | Authentication required |
| `HTTP_403_FORBIDDEN` | 403 | Insufficient permissions |
| `HTTP_404_NOT_FOUND` | 404 | Resource not found |
| `HTTP_405_METHOD_NOT_ALLOWED` | 405 | Wrong HTTP method |
| `HTTP_409_CONFLICT` | 409 | Resource conflict |
| `HTTP_422_UNPROCESSABLE_ENTITY` | 422 | Validation error |
| `HTTP_429_TOO_MANY_REQUESTS` | 429 | Rate limited |
| `HTTP_500_INTERNAL_SERVER_ERROR` | 500 | Server error |
| `HTTP_502_BAD_GATEWAY` | 502 | Upstream error |
| `HTTP_503_SERVICE_UNAVAILABLE` | 503 | Service down |

### Import Cheat Sheet

```python
# Core
from fastapi import FastAPI, APIRouter, Request, Response

# Parameters
from fastapi import Path, Query, Header, Cookie, Body, Form, File, UploadFile

# Dependencies
from fastapi import Depends, Security

# Exceptions
from fastapi import HTTPException, WebSocketException
from fastapi.exceptions import RequestValidationError

# Responses
from fastapi.responses import (
    JSONResponse, HTMLResponse, PlainTextResponse,
    RedirectResponse, StreamingResponse, FileResponse,
    ORJSONResponse, UJSONResponse,
    EventSourceResponse,
)

# Security
from fastapi.security import (
    OAuth2PasswordBearer, OAuth2PasswordRequestForm,
    OAuth2AuthorizationCodeBearer,
    HTTPBasic, HTTPBasicCredentials,
    HTTPBearer, HTTPAuthorizationCredentials,
    APIKeyHeader, APIKeyQuery, APIKeyCookie,
)

# Middleware
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware

# Background tasks
from fastapi import BackgroundTasks

# WebSockets
from fastapi import WebSocket, WebSocketDisconnect

# Testing
from fastapi.testclient import TestClient

# Templates & Static
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles

# Encoding
from fastapi.encoders import jsonable_encoder

# Status codes
from fastapi import status
```

> [!TIP]
> For architecture details, deployment configuration, security internals, and performance tuning, see the [FastAPI Admin Guide](/tiangolo/fastapi/admin-guide/) (Admin Guide section).

---

_Generated May 2026 from tiangolo/fastapi at 9653034. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
