# Referencia Completa — FastAPI Experto

## Middleware Personalizado

```python
import time
import uuid
from fastapi import Request, Response

async def middleware_request_id(request: Request, call_next):
    request_id = str(uuid.uuid4())
    request.state.request_id = request_id
    inicio = time.time()
    response: Response = await call_next(request)
    duracion = time.time() - inicio
    response.headers["X-Request-ID"] = request_id
    response.headers["X-Response-Time"] = f"{duracion:.4f}s"
    return response

app.middleware("http")(middleware_request_id)
```

---

## Testing con httpx

```python
# tests/test_facturas.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app

@pytest.fixture
async def client(db_session):
    async with AsyncClient(
        transport=ASGITransport(app=app),
        base_url="http://test",
    ) as ac:
        yield ac

@pytest.mark.asyncio
async def test_crear_factura(client, token_admin, factura_valida):
    respuesta = await client.post(
        "/api/v1/facturas/",
        json=factura_valida,
        headers={"Authorization": f"Bearer {token_admin}"},
    )
    assert respuesta.status_code == 201
    data = respuesta.json()
    assert data["estatus"] == "borrador"
    assert "id" in data

@pytest.mark.asyncio
async def test_factura_requiere_autenticacion(client):
    respuesta = await client.post("/api/v1/facturas/", json={})
    assert respuesta.status_code == 403  # Sin token
```

---

## Configuración con pydantic-settings

```python
# app/core/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")

    APP_NOMBRE: str = "Mi API"
    APP_VERSION: str = "1.0.0"
    DEBUG: bool = False
    DATABASE_URL: str
    SECRET_KEY: str
    CORS_ORIGINS: list[str] = ["http://localhost:4200"]
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30

settings = Settings()
```
