FastAPI de la zero: configurare, indicații de tip și auto-documentare OpenAPI
Aflați cum să începeți un proiect FastAPI în mai puțin de 5 minute: introduceți indicii ca contract API, Pydantic v2 pentru validarea datelor și documentația Swagger generată automat fără configurație suplimentară.
De ce FastAPI în 2026
FastAPI a trecut de la un proiect experimental la un cadru de referință pentru API-uri în doar câțiva ani Python în producție. Potrivit Python Developers Survey 2024, este al doilea cadru Python web prin difuzie (38%), depășit doar de Django. Motivele sunt concrete: performanta comparabil cu Node.js și Go pentru încărcături de lucru legate de I/O, validare a datelor zero-boilerplate cu Pydantic v2 și documentația OpenAPI generată automat din cod.
Secretul este arhitectural: FastAPI este construit pe baza Starlete (cadru ASGI) e Pydantic (validare cu miez Rust). Nu adaugă abstracții inutile - fiecare caracteristică are un motiv tehnic precis.
Ce vei învăța
- Instalare și primul server FastAPI care rulează în 5 minute
- Cum devin indicii de tip Python validare, serializare și documentare
- Parametri de cale, parametri de interogare și corpuri de solicitare cu Pydantic
- Swagger UI și ReDoc: navigați în documentația generată automat
- Codurile de stare, modelele de răspuns și tratarea de bază a erorilor
- Structura unui proiect FastAPI real (nu doar un singur fișier)
Instalare și primul server
FastAPI necesită Python 3.8+, dar vă recomandăm 3.11+ pentru performanță îmbunătățită de asyncio și noile caracteristici ale indicii de tip. Uvicorn este serverul ASGI recomandat pentru dezvoltarea locală.
# 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
Serverul minim posibil. Salvează asta ca 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.
Deschide http://127.0.0.1:8000/docs pentru a vedea interfața de utilizare Swagger generată
automat. Nu ați configurat nimic: FastAPI a citit adnotările codului
și a generat documentația completă.
Tastați Sugestii ca Contract API
Cea mai puternică caracteristică a FastAPI este utilizarea indicii de tip Python, cum ar fi Definirea contractului API. Fiecare parametru adnotat devine automat: validate, documentate și serializate. Zero cod boilerplate suplimentar.
# 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
Solicitați Caroseria cu Modele Pydantic
Pentru datele din corpul unei solicitări POST/PUT/PATCH, se folosesc șabloane Pydantic. FastAPI deserializează automat JSON-ul primit, îl validează pe șablon, și îl face disponibil ca obiect Python tip.
# 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)
Gestionarea erorilor cu HTTPException
FastAPI mapează excepțiile în răspunsuri HTTP structurate. Modelul standard utilizează
HTTPException pentru erorile așteptate și un handler global de excepții pentru
erori neașteptate.
# 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"},
)
Structura unui proiect real
Un singur main.py este bine pentru tutoriale, dar nu pentru un proiect real.
Structura recomandată separă preocupările în module coezive:
# 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}
Documentație OpenAPI: Swagger și ReDoc
FastAPI generează automat două interfețe de documentare interactive pe care le puteți vizita în timpul dezvoltării:
-
Swagger UI (
/docs): interfață interactivă pentru testare API-ul direct din browser. Acceptă autentificare, corp JSON, parametri de interogare. -
ReDoc (
/redoc): documentatie lizibila, ideala pt partajați cu echipa dvs. sau cu consumatorii API. -
OpenAPI JSON (
/openapi.json): schemă care poate fi citită de mașină folosit pentru a genera clienți SDK în orice limbă.
# 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
Documentatie in productie
Dezactivați întotdeauna Swagger UI și ReDoc în producție prin setare
docs_url=None e redoc_url=None în constructorul FastAPI.
Documentația interactivă expune structura internă a API-ului și poate
să fie utilizat pentru a explora puncte finale nedocumentate. Punctul final
/openapi.json trebuie protejate în același mod.
Configurare cu Setări Pydantic
Modul corect de a gestiona configurația în FastAPI este utilizarea
pydantic-settings care citește automat variabilele de mediu
cu validare de tip:
# 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,
}
Concluzii și pașii următori
FastAPI combină trei caracteristici care, în general, se exclud reciproc: viteza de dezvoltare, siguranța și performanța tipului. Sugestiile de tip nu sunt decorative - conduc validarea, documentare și serializare automată. Acest lucru reduce drastic codul boilerplate comparativ cu Flask sau Django REST Framework.
Următorul pas este să înțelegem modelul asincron care face FastAPI rapid pentru
Sarcini de lucru legate de I/O: cum funcționează asincronia, buclele de evenimente și corutinele și când să le folosiți
async def vs def normal.
Seria Web Python FastAPI și Async
- Articolul 1 (acesta): Configurare, indicații de tip și auto-documentare OpenAPI
- Articolul 2: Async/Await în Python: bucle de evenimente, corutine și concurență legată de I/O
- Articolul 3: Pydantic v2: validare avansată, model de bază și adaptor de tip
- Articolul 4: Injecție de dependență: model pentru cod curat și testabil
- Articolul 5: Baza de date asincronă cu SQLAlchemy 2.0 și Alambic







