FastAPI vanaf nul: installatie, typehints en OpenAPI-zelfdocumentatie
Leer hoe u in minder dan 5 minuten een FastAPI-project start: typ hints als API-contract, Pydantic v2 voor gegevensvalidatie en automatisch gegenereerde Swagger-documentatie zonder extra configuratie.
Waarom FastAPI in 2026
FastAPI is in slechts een paar jaar tijd opgeschaald van een experimenteel project naar een referentiekader voor API's Python in productie. Volgens de Python Developers Survey 2024 is dit het tweede raamwerk Python-web door diffusie (38%), alleen overtroffen door Django. De redenen zijn concreet: prestaties vergelijkbaar met Node.js en Go voor I/O-gebonden workloads, gegevensvalidatie zonder standaardgegevens Pydantic v2 en OpenAPI-documentatie worden automatisch gegenereerd op basis van de code.
Het geheim is architectonisch: er is op FastAPI gebouwd Sterretjes (ASGI-framework) e Pydantisch (validatie met Rust-kern). Het voegt niets toe nutteloze abstracties – elk kenmerk heeft een precieze technische reden.
Wat je gaat leren
- Installatie en eerste FastAPI-server draait in 5 minuten
- Hoe hints van het Python-type validatie, serialisatie en documentatie worden
- Padparameters, queryparameters en verzoekteksten met Pydantic
- Swagger UI en ReDoc: Navigeer door automatisch gegenereerde documentatie
- Statuscodes, responsmodellen en basisfoutafhandeling
- Structuur van een echt FastAPI-project (niet slechts een enkel bestand)
Installatie en eerste server
FastAPI vereist Python 3.8+, maar we raden 3.11+ aan voor betere prestaties van asyncio en de nieuwe kenmerken van typehints. Uvicorn is de ASGI-server aanbevolen voor lokale ontwikkeling.
# 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
De minimaal mogelijke server. Bewaar dit als 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.
Open http://127.0.0.1:8000/docs om de gegenereerde Swagger-gebruikersinterface te bekijken
automatisch. U heeft niets geconfigureerd: FastAPI heeft de codeannotaties gelezen
en genereerde volledige documentatie.
Typ Hints als API-contract
De krachtigste functie van FastAPI is het gebruik van hints van het Python-type, zoals API-contractdefinitie. Elke geannoteerde parameter wordt automatisch: gevalideerd, gedocumenteerd en geserialiseerd. Geen extra standaardcode.
# 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
Lichaam aanvragen met Pydantic-modellen
Voor gegevens in de hoofdtekst van een POST/PUT/PATCH-verzoek worden Pydantic-sjablonen gebruikt. FastAPI deserialiseert automatisch de binnenkomende JSON, valideert deze tegen de sjabloon, en maakt het beschikbaar als een getypt Python-object.
# 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)
Foutafhandeling met HTTPException
FastAPI brengt uitzonderingen in gestructureerde HTTP-reacties in kaart. Het standaardpatroon gebruikt
HTTPException voor verwachte fouten en een globale uitzonderingshandler voor
onverwachte fouten.
# 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"},
)
Structuur van een echt project
Een enkele main.py het is prima voor tutorials, maar niet voor een echt project.
De aanbevolen structuur verdeelt de problemen in samenhangende modules:
# 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}
OpenAPI-documentatie: Swagger en ReDoc
FastAPI genereert automatisch twee interactieve documentatie-interfaces die u kunt bezoeken tijdens de ontwikkeling:
-
Swagger-gebruikersinterface (
/docs): interactieve interface voor testen de API rechtstreeks vanuit de browser. Ondersteunt authenticatie, JSON-body, queryparameters. -
ReDoc (
/redoc): leesbare documentatie, ideaal voor deel met uw team of API-consumenten. -
OpenAPI JSON (
/openapi.json): machinaal leesbaar schema gebruikt om SDK-clients in elke taal te genereren.
# 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
Schakel Swagger UI en ReDoc altijd uit in productie door dit in te stellen
docs_url=None e redoc_url=None in de FastAPI-constructor.
De interactieve documentatie legt de interne structuur van de API bloot en kan
worden gebruikt om ongedocumenteerde eindpunten te verkennen. Het eindpunt
/openapi.json moeten op dezelfde manier beschermd worden.
Configuratie met Pydantic-instellingen
De juiste manier om de configuratie in FastAPI af te handelen is door gebruik te maken van
pydantic-settings die automatisch omgevingsvariabelen leest
met typevalidatie:
# 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,
}
Conclusies en volgende stappen
FastAPI combineert drie functies die elkaar over het algemeen uitsluiten: ontwikkelingssnelheid, typeveiligheid en prestaties. Typehints zijn niet decoratief; ze zorgen voor validatie, documentatie en automatische serialisatie. Dit vermindert de code drastisch standaard vergeleken met Flask of Django REST Framework.
De volgende stap is het begrijpen van het asynchrone model dat FastAPI snel maakt
I/O-gebonden workloads: hoe asyncio, eventloops en coroutines werken, en wanneer ze te gebruiken
async def vs def normaal.
Python FastAPI en Async Web-serie
- Artikel 1 (dit): Installatie, typehints en OpenAPI-zelfdocumentatie
- Artikel 2: Async/Await in Python: gebeurtenislussen, coroutines en I/O-gebonden gelijktijdigheid
- Artikel 3: Pydantic v2: geavanceerde validatie, BaseModel en TypeAdapter
- Artikel 4: Afhankelijkheidsinjectie: patroon voor schone en testbare code
- Artikel 5: Asynchrone database met SQLAlchemy 2.0 en Alembic







