API-first design con FastAPI: dall'OpenAPI al contratto in produzione

L'approccio API-first inverte l'ordine classico: si progetta il contratto prima di scrivere una riga di business logic. FastAPI rende questo approccio naturale — la documentazione OpenAPI, la validazione e i tipi sono generati automaticamente dal codice Python. Ma API-first non è una feature del framework: è una disciplina di design.

Cosa significa davvero API-first

API-first non significa "scrivere le API prima del frontend". Significa trattare l'API come il prodotto principale, non come un layer accessorio. Il contratto — endpoint, payload, errori, versioning — viene definito, revisionato e concordato prima di scrivere business logic o UI. Il codice implementa il contratto, non viceversa.

I vantaggi pratici: frontend e backend possono sviluppare in parallelo con mock; i contratti sono verificabili; le rotture breaking vengono rilevate in review, non in produzione.

Perché FastAPI si presta all'API-first

FastAPI genera automaticamente la spec OpenAPI 3.x dallo stesso codice che esegue le richieste. Non c'è disallineamento tra documentazione e implementazione perché sono la stessa cosa. Pydantic v2 definisce i tipi dei payload con validazione a runtime e serializzazione JSON inclusa — nessun layer di validazione separato da mantenere.

I principi di design che contano

Nomi di risorse
Usare sostantivi plurali: /orders, /users/{id}. Mai verbi nell'URL: /getOrders è sbagliato. I verbi sono già nei metodi HTTP.
Metodi HTTP
GET per lettura idempotente, POST per creazione, PUT/PATCH per aggiornamento (PUT = sostituzione completa, PATCH = aggiornamento parziale), DELETE per eliminazione. Rispettarli — non usare POST per ogni cosa.
Errori strutturati
Usare sempre lo stesso schema di errore: {"error": "NOT_FOUND", "message": "...", "detail": {...}}. Mai restituire stringhe raw come risposta di errore.
Paginazione
Cursor-based per collection large (statica nel tempo, safe per ordini di inserimento), offset per collection piccole o con UI di paginazione numerata. Definirla nel contratto prima di implementarla.
Versioning
Nel path: /v1/orders. Semplice, visibile nei log, cache-friendly. Mantenere V1 funzionante per almeno 6-12 mesi dopo il rilascio di V2 — deprecation graduale, non hard cut.

Pydantic v2: la validazione come tipo

Pydantic v2 è circa 5-10x più veloce della v1 grazie al core in Rust. Per FastAPI, definire modelli Pydantic separati per input (request) e output (response) è una pratica importante: il modello di risposta decide esattamente cosa viene serializzato e inviato al client, prevenendo la fuoriuscita accidentale di campi interni (password hash, ID interni, flag di sistema).

I validator di Pydantic v2 (@field_validator, @model_validator) permettono logiche di validazione complesse che vanno oltre il tipo — regole di business, coerenza tra campi, normalizzazione dei dati in ingresso.

Dependency injection in FastAPI

Il sistema di dependency injection di FastAPI è uno dei suoi punti più sottovalutati. Le dipendenze — connessioni database, autenticazione, permessi, rate limiting — vengono dichiarate come parametri di funzione con Depends(). FastAPI le risolve automaticamente a ogni richiesta.

Il beneficio principale non è la comodità sintattica: è la testabilità. In fase di test, le dipendenze reali (DB, servizi esterni) vengono sostituite da mock tramite app.dependency_overrides, senza modificare nulla nel codice applicativo.

Testing con httpx: test senza server

FastAPI è testabile con httpx.AsyncClient e app come transport — nessun server HTTP reale necessario, zero latenza di rete, test deterministici. La struttura ideale:

  • Unit test: testano singole funzioni Pydantic e business logic pura.
  • Integration test con httpx: testano l'endpoint completo con dependency override per il DB (database di test, non mock).
  • Contract test: verificano che lo schema OpenAPI generato non abbia breaking change rispetto alla versione precedente (tool: openapi-diff).
Sul versioning delle API

Una breaking change è qualsiasi modifica che richiede aggiornamenti al codice client per continuare a funzionare: rimozione di un campo, cambio di tipo, cambio del formato di un errore, cambio dei codici HTTP. Aggiungere campi opzionali non è breaking. Cambiare il nome di un campo esistente lo è sempre. Definire in anticipo cosa è breaking e cosa non lo è — scritta nel CONTRIBUTING del progetto — salva mesi di debugging integrazioni in produzione.

Autenticazione: non reinventarla

FastAPI ha security scheme nativi per OAuth2, JWT Bearer e API key. Per API B2B, JWT con RS256 (firma asimmetrica) è lo standard: il provider genera i token firmati con la chiave privata, i consumer li verificano con la chiave pubblica senza chiamate aggiuntive al provider. Per API di servizi interni, API key con header X-API-Key è sufficiente — semplice da rotare, da revocare, da tracciare nei log.

OpenAPI come fonte di verità

La spec OpenAPI generata da FastAPI non è solo documentazione: è la fonte di verità del contratto. Usarla per generare client SDK (openapi-generator), per eseguire contract testing automatico in CI, per validare che le breaking change siano intenzionali. Un CI che fallisce quando la spec cambia in modo incompatibile è il guard rail che serve.