OpenAPI e Swagger per Laravel: documentazione API generata dal codice e sempre aggiornata
Ogni API che ho costruito o ereditato nei vent'anni di carriera ha avuto lo stesso problema: la documentazione diverge dall'implementazione. Il giorno del go-live, lo spec OpenAPI è perfettamente allineato al codice. Un mese dopo, due endpoint sono stati aggiunti senza aggiornare lo spec. Tre mesi dopo, un campo nella risposta è stato rinominato ma il documento Swagger mostra ancora il nome vecchio. Sei mesi dopo, gli integratori che consumano l'API - partner commerciali, applicazioni mobile, sistemi terzi - stanno lavorando su una documentazione che descrive un'API che non esiste più. Il risultato: ticket di supporto, email di frustrazione, e sviluppatori che aggirano la documentazione ufficiale e reverse-engineerano l'API direttamente con Postman.
Nel 2024 ho adottato un approccio radicalmente diverso per un cliente del settore servizi B2B che espone un'API Laravel con 86 endpoint consumata da 14 integratori esterni: la documentazione OpenAPI viene generata automaticamente dal codice PHP ad ogni build CI, validata contro i test di integrazione, e pubblicata solo se lo spec e l'implementazione sono allineati. Non esiste un documento separato da mantenere - il codice è la documentazione. Se uno sviluppatore aggiunge un endpoint senza l'annotation OpenAPI, la pipeline CI fallisce e il merge viene bloccato. Se uno sviluppatore modifica la risposta di un endpoint senza aggiornare l'annotation, il test di validazione fallisce. Il risultato: in 14 mesi di produzione, nessun integratore ha mai segnalato una discrepanza tra la documentazione e il comportamento reale dell'API.
Perché l'approccio code-first è l'unico che funziona su scala?
Esistono due approcci alla documentazione API con OpenAPI: design-first (scrivi lo spec prima del codice, poi implementi l'API conforme allo spec) e code-first (scrivi il codice con annotation, poi generi lo spec dal codice). Il design-first ha il vantaggio della progettazione consapevole - definisci il contratto prima dell'implementazione - ma ha un problema pratico insuperabile nelle PMI italiane: richiede disciplina continua per mantenere lo spec allineato al codice dopo il go-live. In un team di 3-5 sviluppatori senza un API architect dedicato, lo spec design-first diventa stale in settimane perché nessuno si ricorda di aggiornarlo dopo ogni modifica al controller.
Il code-first inverte il problema: lo spec è un prodotto derivato del codice, non un documento indipendente. Lo sviluppatore scrive le annotation OpenAPI nello stesso file del controller - una riga sopra il metodo, non in un file separato - e un generatore automatico produce lo spec JSON o YAML ad ogni build. Se il codice cambia, lo spec cambia automaticamente. Se lo spec non corrisponde al comportamento reale (un'annotation che dichiara un campo string ma il codice restituisce un integer), il test di validazione lo cattura prima del merge. Nel mio profilo professionale trovi il dettaglio dell'esperienza che porto nella progettazione di API documentate - un'area dove la differenza tra "documentazione che esiste" e "documentazione che serve" sta nel workflow che la genera e la valida.
Implementazione con Scramble: lo strumento che ha cambiato il mio workflow
Per Laravel, lo strumento che uso dal 2024 è Scramble - un generatore OpenAPI code-first che analizza i controller Laravel, i FormRequest, i Resource e i tipi di ritorno PHP per produrre uno spec OpenAPI 3.1 completo senza bisogno di annotation manuali nella maggior parte dei casi. A differenza dei generatori basati su annotation (come darkaonline/l5-swagger che richiede docblock PHPDoc o attribute PHP 8), Scramble inferisce lo schema delle request e response direttamente dal codice - i tipi dei parametri, le regole di validazione del FormRequest, la struttura dell'API Resource.
L'installazione è un singolo comando Composer:
composer require dedoc/scramble
# La dashboard è accessibile a /docs/api
# Lo spec OpenAPI è a /docs/api.jsonUn controller Laravel tipico produce documentazione automatica senza nessuna annotation aggiuntiva:
// app/Http/Controllers/Api/OrdineController.php
class OrdineController extends Controller
{
/**
* Lista ordini con paginazione e filtri.
*
* Restituisce gli ordini dell'utente autenticato,
* filtrabili per stato e data.
*/
public function index(OrdineIndexRequest $request): OrdineCollection
{
$ordini = Ordine::query()
->where('cliente_id', $request->user()->cliente_id)
->when($request->stato, fn ($q, $s) => $q->where('stato', $s))
->when($request->da, fn ($q, $d) => $q->where('created_at', '>=', $d))
->orderByDesc('created_at')
->paginate($request->per_page ?? 25);
return new OrdineCollection($ordini);
}
}
// app/Http/Requests/OrdineIndexRequest.php
class OrdineIndexRequest extends FormRequest
{
public function rules(): array
{
return [
'stato' => ['nullable', 'string', 'in:bozza,confermato,spedito,consegnato'],
'da' => ['nullable', 'date', 'before_or_equal:today'],
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
];
}
}Da questo codice, Scramble genera automaticamente uno spec OpenAPI che descrive l'endpoint /api/ordini con metodo GET, i parametri di query stato (enum con i valori ammessi), da (formato date), e per_page (integer con min/max), la risposta paginata con la struttura dell'OrdineResource, e gli schemi di errore per validazione fallita (422) e non autenticato (401). Lo sviluppatore non ha scritto una sola riga di documentazione - ha scritto codice PHP con tipi e validazione, e Scramble ha derivato lo spec.
Validazione dello spec nei test: la rete di sicurezza
Generare lo spec dal codice è necessario ma non sufficiente: bisogna anche verificare che lo spec generato corrisponda al comportamento reale dell'API. La validazione avviene nei test di integrazione con un middleware che confronta la risposta HTTP effettiva con lo schema dichiarato nello spec OpenAPI:
// tests/Feature/Api/OrdineTest.php
use Spectator\Spectator; // spectator/spectator-laravel
class OrdineTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// Valida ogni risposta contro lo spec OpenAPI generato
Spectator::using('api-docs.json');
}
public function test_lista_ordini_rispetta_lo_spec(): void
{
$cliente = Cliente::factory()->create();
Ordine::factory()->count(5)->create(['cliente_id' => $cliente->id]);
$this->actingAs($cliente->user)
->getJson('/api/ordini?stato=confermato&per_page=10')
->assertStatus(200)
->assertValidRequest() // Verifica che la request sia conforme
->assertValidResponse(); // Verifica che la response sia conforme
}
}Se il test aggiunge un campo alla risposta che non è dichiarato nello spec, assertValidResponse() fallisce. Se un endpoint accetta un parametro non documentato, assertValidRequest() fallisce. Questa validazione bidirezionale - request E response - è ciò che garantisce che lo spec OpenAPI sia una rappresentazione fedele dell'API reale, non un'approssimazione ottimistica scritta dal documentatore.
La pipeline CI: generazione, validazione e pubblicazione automatica
Il ciclo completo nella pipeline CI/CD è questo: ad ogni push, il test runner esegue i test con la validazione Spectator, il generatore Scramble produce lo spec aggiornato, un linter OpenAPI (spectral o redocly) verifica che lo spec sia valido e rispetti le convenzioni del progetto, e se tutto passa lo spec viene pubblicato sulla dashboard documentazione. Se il test di validazione fallisce - perché il codice è cambiato senza aggiornare le annotation o viceversa - il merge viene bloccato e lo sviluppatore deve allineare codice e spec prima di procedere.
# .github/workflows/api-docs.yml
name: API Documentation
on: [push]
jobs:
validate-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Installa dipendenze
run: composer install --no-interaction
- name: Esegui test con validazione OpenAPI
run: php artisan test --filter=Api
- name: Genera spec OpenAPI aggiornato
run: php artisan scramble:export > docs/openapi.json
- name: Valida spec con Spectral
run: npx @stoplight/spectral-cli lint docs/openapi.json
- name: Pubblica documentazione
if: github.ref == 'refs/heads/main'
run: |
# Copia lo spec sulla dashboard Swagger UI
cp docs/openapi.json public/docs/openapi.jsonQuando le annotation manuali sono necessarie: i limiti dell'inferenza automatica
Scramble è eccellente nel derivare lo spec dai tipi PHP e dalle regole di validazione, ma ci sono casi in cui l'inferenza automatica non basta e servono annotation esplicite. Il primo caso è quando il controller restituisce risposte condizionali - ad esempio, un endpoint che restituisce una struttura diversa per utenti admin e utenti normali. Scramble non può inferire questa condizionalità dal codice PHP e genera solo la struttura di una delle due varianti. La soluzione è un annotation PHPDoc con @response che descrive entrambe le varianti esplicitamente.
Il secondo caso riguarda gli endpoint con upload di file e risposte binarie (download di PDF, export CSV, generazione di immagini). Scramble non può inferire il content-type della risposta da un return response()->download(), e lo spec generato dichiara il tipo come application/json di default. Serve un'annotation @response 200 {"content": {"application/pdf": {}}} per documentare correttamente il tipo di risposta.
Il terzo caso è la documentazione degli errori di business. Scramble genera automaticamente le risposte 401 (non autenticato), 403 (non autorizzato) e 422 (validazione fallita) perché sono standard Laravel. Ma gli errori di business specifici dell'applicazione - un 409 per conflitto di stato dell'ordine, un 402 per credito insufficiente, un 429 per rate limiting personalizzato - richiedono annotation manuali perché non sono inferibili dalla struttura del codice.
La regola pratica che applico è: lascia che Scramble generi il 70-80% dello spec automaticamente, e aggiungi annotation manuali solo per i casi che l'inferenza non copre. Ogni annotation manuale è un punto di potenziale disallineamento futuro (se qualcuno modifica il codice senza aggiornare l'annotation), quindi minimizzare le annotation manuali riduce la superficie di rischio di documentation drift. Nei 14 mesi di produzione dell'API del cliente B2B, le annotation manuali sono il 12% del totale - un rapporto che dimostra che l'inferenza automatica copre la grande maggioranza dei casi d'uso reali.
L'impatto sugli integratori: dalla frustrazione alla produttività
L'effetto più tangibile di una documentazione API sempre aggiornata non è tecnico - è relazionale. Prima dell'implementazione del workflow code-first, il team di supporto del cliente dedicava circa 8 ore alla settimana a rispondere a domande degli integratori che derivavano da documentazione errata: "Lo spec dice che il campo importo è un integer ma in realtà ricevo un float", "L'endpoint /api/ordini/{id}/tracking non esiste nello spec ma funziona se lo chiamo", "La risposta include un campo metadata che non è documentato da nessuna parte." Dopo l'implementazione, queste domande sono scese a zero perché lo spec è garantito essere allineato all'implementazione - se un campo esiste nella risposta, è documentato nello spec; se un endpoint esiste nel codice, è presente nella documentazione.
Un effetto secondario che non avevo previsto è stato l'accelerazione dell'onboarding di nuovi integratori. Prima, un nuovo partner che voleva integrare l'API impiegava in media 2-3 settimane per completare la prima integrazione funzionante, con 5-7 scambi di email con il team di supporto per chiarire discrepanze nella documentazione. Dopo l'implementazione dello spec code-first con Swagger UI accessibile dalla dashboard, il tempo di onboarding è sceso a 5-7 giorni con 1-2 scambi di email - un miglioramento del 70% che il team commerciale ha notato perché i partner iniziano a generare fatturato più velocemente.
Per il cliente B2B, questa pipeline ha eliminato completamente la classe di bug "documentazione non aggiornata" dal backlog di supporto. Prima dell'implementazione, il team riceveva in media 3-4 ticket al mese da integratori che segnalavano discrepanze tra lo spec e il comportamento dell'API. Dopo, zero. Il costo dell'implementazione - un giorno di lavoro per configurare Scramble, Spectator e la pipeline CI - si è ripagato nel primo mese con il tempo risparmiato dal team di supporto.
Ho documentato un approccio complementare alla qualità del codice API nel mio articolo sulla code review automatizzata con LLM nelle pipeline GitHub, dove il bot di review verifica anche la presenza delle annotation OpenAPI sugli endpoint nuovi. La combinazione di review LLM per le annotation mancanti e validazione Spectator per le annotation errate crea un sistema di quality gate a due livelli che rende quasi impossibile rilasciare un endpoint non documentato o documentato in modo errato. Se la tua API Laravel ha una documentazione Swagger scritta a mano che non corrisponde più al comportamento reale, contattami per implementare il workflow code-first: in una giornata configuriamo Scramble, scriviamo i test di validazione Spectator per gli endpoint principali, e impostiamo la pipeline CI che da quel momento terrà spec e codice perennemente allineati - un investimento di un giorno che elimina una classe intera di problemi di comunicazione tra team di sviluppo e integratori.