# FastAPI Essentials

## 1. Why FastAPI

FastAPI builds HTTP APIs from **type-hinted Python functions**: it validates requests with Pydantic, serializes responses, generates **OpenAPI** docs (`/docs`) automatically, and runs on ASGI servers (uvicorn) with first-class `async` support.

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

app = FastAPI()

class ItemIn(BaseModel):
    name: str = Field(min_length=1)
    price: float = Field(gt=0)

class ItemOut(ItemIn):
    id: int

@app.post("/items", response_model=ItemOut, status_code=status.HTTP_201_CREATED)
async def create_item(item: ItemIn, repo: "ItemRepo" = Depends(get_repo)):
    return await repo.add(item)

@app.get("/items/{item_id}", response_model=ItemOut)
async def get_item(item_id: int, repo: "ItemRepo" = Depends(get_repo)):
    item = await repo.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item
```

- **Path parameters** (`item_id: int`) are parsed and validated from the URL; **query parameters** are other simple-typed arguments; a Pydantic model argument is the **JSON body**.
- Invalid input returns **422** with field-level errors automatically.
- `response_model` filters and validates output, so internal fields never leak.

## 2. Dependency Injection with Depends

Dependencies are functions (sync, async, or generators) declared as parameter defaults. FastAPI resolves them per request, **caches** each dependency within a request, and supports **sub-dependencies**:

```python
async def get_db():
    async with SessionLocal() as session:   # setup
        yield session                         # injected value
    # teardown after the response (session closed)

async def get_current_user(token: str = Depends(oauth2_scheme), db = Depends(get_db)):
    user = await authenticate(db, token)
    if not user:
        raise HTTPException(401, "Invalid token", headers={"WWW-Authenticate": "Bearer"})
    return user
```

Use dependencies for DB sessions, authentication, pagination parameters and settings. Override them in tests with `app.dependency_overrides[get_db] = fake_db`.

## 3. async def vs def

- `async def` endpoints run on the event loop: only use **non-blocking** libraries inside (`httpx.AsyncClient`, `asyncpg`, async SQLAlchemy).
- Plain `def` endpoints run in a **thread pool**, so blocking libraries (requests, sync DB drivers) are fine there.
- A blocking call inside `async def` freezes every request on that worker.

## 4. Structure and Production Concerns

- Split into **routers** (`APIRouter(prefix="/orders", tags=["orders"])`) per feature; keep business logic in services, not in endpoints.
- Configuration via `pydantic-settings` (env vars); one place for settings, injected with `Depends`.
- **Lifespan** events (`@asynccontextmanager` lifespan) to create and close connection pools.
- Middleware for CORS, request IDs, timing; exception handlers for consistent error bodies.
- Run with several workers (`uvicorn --workers 4` or gunicorn with uvicorn workers) behind a reverse proxy; background work goes to a task queue (Celery, arq, RQ), not `BackgroundTasks` for anything important.
- Test with `TestClient` (sync) or `httpx.AsyncClient` with `ASGITransport`.

## Common Pitfalls

1. **Blocking calls in `async def`** endpoints.
2. **Returning ORM objects without `response_model`**, leaking fields.
3. **Creating a DB connection per request** without pooling.
4. **Business logic inside endpoints**, making it untestable without HTTP.
5. **Heavy work in `BackgroundTasks`**: it runs in the same process and is lost on restart.

## Quick Reference

```text
path params (typed) · query params · Pydantic body · 422 on validation errors · response_model filters output
Depends: per-request, cached, sub-dependencies, yield for setup/teardown · dependency_overrides in tests
async def → only non-blocking I/O · def → thread pool · routers per feature · lifespan for pools
multiple workers behind a proxy · real task queue for background jobs · /docs from OpenAPI
```
