Dlaczego FastAPI w 2026 roku

W ciągu zaledwie kilku lat FastAPI przekształciło się z projektu eksperymentalnego w platformę referencyjną dla interfejsów API Python w produkcji. Według badania Python Developers Survey 2024 jest to drugi framework Sieć Pythona pod względem dyfuzji (38%), ustępuje jedynie Django. Powody są konkretne: wydajność porównywalne z obciążeniami związanymi z Node.js i Go for I/O, zerowa weryfikacja danych standardowych za pomocą Dokumentacja Pydantic v2 i OpenAPI jest automatycznie generowana z kodu.

Sekret tkwi w architekturze: FastAPI jest na niej zbudowane Gwiazdeczki (ramy ASGI) mi Pydantyczny (walidacja z rdzeniem Rust). To nie dodaje bezużyteczne abstrakcje – każda cecha ma dokładne uzasadnienie techniczne.

Czego się nauczysz

  • Instalacja i uruchomienie pierwszego serwera FastAPI w 5 minut
  • Jak wskazówki dotyczące typów Pythona stają się walidacją, serializacją i dokumentacją
  • Parametry ścieżki, parametry zapytań i treści żądań w Pydantic
  • Interfejs Swagger i ReDoc: przeglądaj automatycznie wygenerowaną dokumentację
  • Kody stanu, modele odpowiedzi i podstawowa obsługa błędów
  • Struktura prawdziwego projektu FastAPI (nie tylko pojedynczego pliku)

Instalacja i pierwszy serwer

FastAPI wymaga Pythona w wersji 3.8 lub nowszej, ale w celu poprawy wydajności zalecamy wersję 3.11 lub nowszą asyncio i nowe funkcje podpowiedzi typu. Uvicorn jest serwerem ASGI rekomendowane dla rozwoju lokalnego.

# Ambiente virtuale (sempre, mai installare globalmente)
python -m venv .venv
source .venv/bin/activate  # Linux/Mac
# .venv\Scripts\activate  # Windows

# Installazione dipendenze core
pip install fastapi uvicorn[standard]

# Per sviluppo aggiungere anche:
pip install httpx pytest pytest-asyncio

# Verifica installazione
python -c "import fastapi; print(fastapi.__version__)"
# 0.115.x

Minimalny możliwy serwer. Zapisz to jako main.py:

# main.py - Il server FastAPI piu semplice possibile
from fastapi import FastAPI

# Crea l'istanza dell'applicazione
# title e description appaiono nella documentazione Swagger
app = FastAPI(
    title="La Mia API",
    description="API costruita con FastAPI e Python type hints",
    version="1.0.0",
)

# Decorator che registra la route GET /
@app.get("/")
def read_root():
    # Il dict viene automaticamente serializzato in JSON
    return {"message": "Hello, FastAPI!", "status": "running"}

# Avvio con: uvicorn main:app --reload
# main = nome del file, app = variabile FastAPI, --reload = hot reload
# Avvia il server con hot reload (ricrea il server ad ogni modifica)
uvicorn main:app --reload

# Output:
# INFO:     Will watch for changes in these directories: ['/path/to/project']
# INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
# INFO:     Started reloader process [12345]
# INFO:     Started server process [12346]
# INFO:     Waiting for application startup.
# INFO:     Application startup complete.

Otwarte http://127.0.0.1:8000/docs aby zobaczyć wygenerowany interfejs Swagger automatycznie. Nic nie skonfigurowałeś: FastAPI odczytał adnotacje kodu i wygenerował kompletną dokumentację.

Wpisz Wskazówki jako kontrakt API

Najpotężniejszą funkcją FastAPI jest użycie podpowiedzi typu Python, takich jak Definicja kontraktu API. Każdy oznaczony parametr automatycznie staje się: sprawdzone, udokumentowane i serializowane. Zero dodatkowego kodu standardowego.

# routes/users.py - Esempi di parametri con type hints
from fastapi import FastAPI, Path, Query, HTTPException
from typing import Optional
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime

app = FastAPI()

# --- PATH PARAMETERS ---
# Il tipo int fa si che FastAPI validi che userId sia un intero
@app.get("/users/{user_id}")
def get_user(
    user_id: int,  # Path parameter: automaticamente estratto dall'URL
):
    if user_id <= 0:
        raise HTTPException(status_code=400, detail="user_id must be positive")
    return {"user_id": user_id, "name": f"User {user_id}"}

# Path con validazione avanzata tramite Path()
@app.get("/items/{item_id}")
def get_item(
    item_id: int = Path(
        title="The ID of the item",
        description="Must be a positive integer",
        ge=1,       # greater than or equal to 1
        le=1000,    # less than or equal to 1000
    ),
):
    return {"item_id": item_id}

# --- QUERY PARAMETERS ---
# Parametri con default = opzionali, senza default = obbligatori
@app.get("/search")
def search_users(
    q: str,                          # Obbligatorio (no default)
    page: int = 1,                   # Opzionale con default
    limit: int = Query(default=10, ge=1, le=100),  # Con validazione
    active_only: bool = True,        # Bool viene da "true"/"false" nella query string
    role: Optional[str] = None,      # Opzionale, None se non fornito
):
    return {
        "query": q,
        "page": page,
        "limit": limit,
        "active_only": active_only,
        "role": role,
    }
    # GET /search?q=mario&page=2&limit=20&active_only=false&role=admin

Treść żądania z modelami Pydantic

W przypadku danych w treści żądania POST/PUT/PATCH używane są szablony Pydantic. FastAPI automatycznie deserializuje przychodzący JSON, sprawdza go względem szablonu, i udostępnia go jako obiekt Pythona z określonym typem.

# models/user.py - Definizione dei modelli con Pydantic v2
from pydantic import BaseModel, EmailStr, Field, field_validator
from typing import Optional
from enum import Enum

class UserRole(str, Enum):
    admin = "admin"
    editor = "editor"
    viewer = "viewer"

# Modello per la creazione di un utente (in input)
class UserCreate(BaseModel):
    name: str = Field(
        min_length=2,
        max_length=100,
        description="Full name of the user",
        examples=["Mario Rossi"],
    )
    email: EmailStr  # Validazione email inclusa in Pydantic
    role: UserRole = UserRole.viewer  # Default al valore meno privilegiato
    age: Optional[int] = Field(default=None, ge=0, le=150)

    # Validator personalizzato (Pydantic v2 syntax)
    @field_validator("name")
    @classmethod
    def name_must_contain_space(cls, v: str) -> str:
        if " " not in v:
            raise ValueError("name must contain at least first and last name")
        return v.strip()

# Modello per la risposta (include campi generati dal server)
class UserResponse(BaseModel):
    id: int
    name: str
    email: EmailStr
    role: UserRole
    created_at: str  # ISO 8601

# Route POST che usa i modelli
@app.post(
    "/users",
    response_model=UserResponse,  # Definisce la struttura della risposta
    status_code=201,               # HTTP 201 Created
    summary="Create a new user",
    tags=["users"],
)
def create_user(user: UserCreate):
    # user e gia validato e typed come UserCreate
    # FastAPI ha deserializzato il JSON e verificato tutti i vincoli
    new_user = {
        "id": 42,  # In realta verrebbe dal database
        "name": user.name,
        "email": user.email,
        "role": user.role.value,
        "created_at": "2026-06-01T10:00:00Z",
    }
    return new_user
    # Solo i campi di UserResponse vengono inclusi nella risposta
    # (response_model filtra automaticamente campi extra come password hash)

Obsługa błędów z wyjątkiem HTTPException

FastAPI mapuje wyjątki na ustrukturyzowane odpowiedzi HTTP. Używany jest standardowy wzór HTTPException dla oczekiwanych błędów i globalną procedurę obsługi wyjątków dla nieoczekiwane błędy.

# Errori standard con HTTPException
from fastapi import HTTPException, status

@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
    user = db.find_user(user_id)  # Ipotetica funzione DB

    if user is None:
        # status.HTTP_404_NOT_FOUND = 404 (uso le costanti, piu leggibile)
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"User with id {user_id} not found",
        )

    return user

# Exception handler globale per errori non gestiti
from fastapi import Request
from fastapi.responses import JSONResponse

@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
    # Log l'errore (NON esporre il dettaglio in produzione)
    import logging
    logging.error(f"Unhandled exception: {exc}", exc_info=True)

    return JSONResponse(
        status_code=500,
        content={"detail": "Internal server error"},
    )

Struktura prawdziwego projektu

Singiel main.py jest w porządku do tutoriali, ale nie do prawdziwego projektu. Zalecana struktura dzieli obawy na spójne moduły:

# Struttura consigliata per un progetto FastAPI medio
my_api/
├── app/
│   ├── __init__.py
│   ├── main.py              # Entry point: crea l'app FastAPI e include i router
│   ├── config.py            # Configurazione (env vars, Settings con Pydantic)
│   ├── database.py          # Connessione DB, session factory
│   ├── models/
│   │   ├── __init__.py
│   │   ├── user.py          # SQLAlchemy ORM models
│   │   └── item.py
│   ├── schemas/             # Pydantic models (request/response)
│   │   ├── __init__.py
│   │   ├── user.py          # UserCreate, UserUpdate, UserResponse
│   │   └── item.py
│   ├── routers/             # APIRouter per ogni dominio
│   │   ├── __init__.py
│   │   ├── users.py         # /users endpoints
│   │   └── items.py         # /items endpoints
│   ├── services/            # Business logic (separata dai router)
│   │   ├── user_service.py
│   │   └── item_service.py
│   └── dependencies.py      # Depends() condivise (auth, DB session)
├── tests/
│   ├── conftest.py
│   ├── test_users.py
│   └── test_items.py
├── alembic/                 # Migrations DB
├── pyproject.toml
└── docker-compose.yml
# app/main.py - Entry point con router modulari
from fastapi import FastAPI
from app.routers import users, items
from app.config import get_settings

settings = get_settings()

app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    docs_url="/docs" if settings.DEBUG else None,  # Disabilita docs in produzione
    redoc_url="/redoc" if settings.DEBUG else None,
)

# Include i router con prefisso e tag per la documentazione
app.include_router(users.router, prefix="/users", tags=["users"])
app.include_router(items.router, prefix="/items", tags=["items"])

@app.get("/health", tags=["system"])
def health_check():
    return {"status": "healthy", "version": settings.APP_VERSION}

Dokumentacja OpenAPI: Swagger i ReDoc

FastAPI automatycznie generuje dwa interaktywne interfejsy dokumentacji, które możesz odwiedzić podczas opracowywania:

  • Swaggerowy interfejs użytkownika (/docs): interaktywny interfejs do testowania API bezpośrednio z przeglądarki. Obsługuje uwierzytelnianie, treść JSON, parametry zapytania.
  • ReDoc (/redoc): czytelna dokumentacja, idealna do udostępnij swojemu zespołowi lub konsumentom API.
  • OpenAPI JSON (/openapi.json): schemat do odczytu maszynowego używany do generowania klientów SDK w dowolnym języku.
# Arricchire la documentazione con metadata aggiuntivi
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()

# Override del schema OpenAPI per aggiungere security schemes e metadata
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema

    openapi_schema = get_openapi(
        title="My API",
        version="2.0.0",
        description="Descrizione lunga dell'API con **markdown** supportato",
        terms_of_service="https://example.com/terms",
        contact={
            "name": "Federico Calo",
            "url": "https://federicocalo.dev",
            "email": "info@federicocalo.dev",
        },
        license_info={
            "name": "MIT",
            "url": "https://opensource.org/licenses/MIT",
        },
        routes=app.routes,
    )

    # Aggiungi Bearer token authentication
    openapi_schema["components"]["securitySchemes"] = {
        "BearerAuth": {
            "type": "http",
            "scheme": "bearer",
            "bearerFormat": "JWT",
        }
    }

    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

Dokumentacja w produkcji

Zawsze wyłączaj interfejs Swagger UI i ReDoc w środowisku produkcyjnym, ustawiając docs_url=None e redoc_url=None w konstruktorze FastAPI. Interaktywna dokumentacja przedstawia wewnętrzną strukturę interfejsu API i może można używać do eksploracji nieudokumentowanych punktów końcowych. Punkt końcowy /openapi.json muszą być chronione w ten sam sposób.

Konfiguracja za pomocą ustawień Pydantic

Prawidłowym sposobem obsługi konfiguracji w FastAPI jest użycie pydantic-settings który automatycznie odczytuje zmienne środowiskowe z walidacją typu:

# app/config.py - Configurazione type-safe con pydantic-settings
# pip install pydantic-settings
from pydantic_settings import BaseSettings, SettingsConfigDict
from functools import lru_cache

class Settings(BaseSettings):
    # Valori letti da variabili d'ambiente (case insensitive)
    APP_NAME: str = "My FastAPI App"
    APP_VERSION: str = "1.0.0"
    DEBUG: bool = False

    # Database
    DATABASE_URL: str  # Obbligatorio: fallisce se non presente
    DB_POOL_SIZE: int = 10
    DB_MAX_OVERFLOW: int = 20

    # Secrets
    SECRET_KEY: str  # Obbligatorio
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30

    # CORS
    ALLOWED_ORIGINS: list[str] = ["http://localhost:3000"]

    model_config = SettingsConfigDict(
        env_file=".env",           # Legge da .env se presente
        env_file_encoding="utf-8",
        case_sensitive=False,      # DATABASE_URL = database_url
    )

# lru_cache evita di rileggere il file .env ad ogni richiesta
@lru_cache
def get_settings() -> Settings:
    return Settings()

# Uso nelle route tramite Depends()
from fastapi import Depends

@app.get("/info")
def app_info(settings: Settings = Depends(get_settings)):
    return {
        "name": settings.APP_NAME,
        "version": settings.APP_VERSION,
        "debug": settings.DEBUG,
    }

Wnioski i dalsze kroki

FastAPI łączy w sobie trzy cechy, które generalnie się wykluczają: szybkość programowania, bezpieczeństwo typu i wydajność. Wskazówki dotyczące typów nie mają charakteru dekoracyjnego — służą do sprawdzania poprawności, dokumentacji i automatycznej serializacji. To drastycznie zmniejsza kod szablon w porównaniu do Flask lub Django REST Framework.

Następnym krokiem jest zrozumienie modelu asynchronicznego, dzięki któremu FastAPI jest szybkie Obciążenia związane z we/wy: jak działają asyncio, pętle zdarzeń i współprogramy oraz kiedy ich używać async def vs def normalna.

Seria Python FastAPI i Async Web

  • Artykuł 1 (ten): Konfiguracja, wskazówki dotyczące wpisywania i dokumentacja OpenAPI
  • Artykuł 2: Async/Await w Pythonie: pętle zdarzeń, współprogramy i współbieżność związana z we/wy
  • Artykuł 3: Pydantic v2: Zaawansowana walidacja, BaseModel i TypeAdapter
  • Artykuł 4: Wstrzykiwanie zależności: wzór czystego i testowalnego kodu
  • Artykuł 5: Asynchroniczna baza danych z SQLAlchemy 2.0 i Alembic