> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neuraltrust.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Python middleware

> TrustGuard on the request path in FastAPI (same client for Django / Flask).

Middleware covers **input**. Guard completions in the route with `direction="output"`
([Python SDK](/trustguard/integrations/python-sdk)).

## Coverage

| Surface    | Monitor | Block | Redact |
| ---------- | :-----: | :---: | :----: |
| LLM input  |    ✅    |   ⚠️  |   ⚠️   |
| LLM output |    ✅    |   ⚠️  |   ⚠️   |
| Tool-level |    ⚠️   |   ⚠️  |   ⚠️   |

**Ask** — `status` is advisory: nothing prompts anyone unless your code does.
A middleware has no user to prompt on the HTTP path either.

**Use it when** you want every new AI route covered by configuration rather than
by someone remembering to add the check. **Not as your only defence when**
traffic reaches a model without an HTTP request — background jobs, queue
consumers, internal calls all bypass it; those need the
[SDK](/trustguard/integrations/python-sdk) at the call site.

**Limits.** Use the async client in async frameworks, or every evaluation blocks
the event loop. Scope the middleware to the AI routes so unrelated endpoints do
not pay the latency. Output coverage needs a second evaluation on the response.

Full comparison: [Coverage](/trustguard/integrations/coverage).

```python theme={null}
import json

from fastapi import FastAPI, Request
from starlette.responses import JSONResponse
from trustguard import AsyncTrustGuard

app = FastAPI()
client = AsyncTrustGuard("<your-trustguard-url>", api_key="<collector-api-key>")

@app.middleware("http")
async def trustguard_middleware(request: Request, call_next):
    if request.method == "POST":
        body = await request.body()
        response = await client.guard(
            {"input": body.decode()},
            direction="input",
            consumer_id=request.headers.get("x-user-id", ""),
            session_id=request.cookies.get("session_id", ""),
        )
        if response.is_blocked:
            return JSONResponse({"detail": "Blocked by TrustGuard"}, status_code=403)
        if response.transformed_payload:
            request._body = json.dumps(response.transformed_payload).encode()
    return await call_next(request)
```
