OpenAPI è un formato per descrivere un'API HTTP in un normale file YAML o JSON. Nel file sono elencati i percorsi che l'API espone, i metodi che ciascuno accetta, i campi che viaggiano nella richiesta e ciò che la risposta restituisce. Il file è leggibile sia da una persona sia da un programma, e dalla stessa sorgente si ricavano documentazione, librerie client e server simulati per i test.

Il nome ufficiale è OpenAPI Specification, in breve OAS. La mantiene la OpenAPI Initiative, un progetto a governance aperta sotto la Linux Foundation.

Qui di seguito: com'è costruito il documento, in cosa OpenAPI differisce da Swagger, cosa è cambiato nelle versioni 3.1 e 3.2 e perché la promessa centrale del formato — una documentazione che non invecchia mai — da sola non si realizza.

Com'è fatto un documento OpenAPI

Un file funzionante è più corto di quanto ci si aspetti:

openapi: 3.1.0

info:
  title: Link
  version: "1.0"

paths:
  /links:
    post:
      summary: Crea un link breve
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
      responses:
        '201':
          description: Link creato

I campi obbligatori al livello più alto sono soltanto due, openapi e info; inoltre il documento deve contenere almeno uno fra paths, components e webhooks. È così che lo definisce la specifica OAS 3.1.1.

Già in queste prime righe si nasconde una trappola: openapi e info.version sono due cose diverse. Il primo è la versione del formato stesso, quella che gli strumenti leggono per capire come interpretare il documento. Il secondo è la versione della vostra API. La specifica lo dice in modo esplicito, ma l'intuito suggerisce il contrario, ed ecco spiegati i file che dichiarano openapi: 1.0.

Le specifiche di una certa dimensione vengono di norma divise su più file con $ref: il documento radice punta a ./paths/links.yaml, che a sua volta punta a ./schemas/link.yaml. Comodo, e all'origine di un problema su cui torno alla fine.

OpenAPI e Swagger non sono la stessa cosa

È la confusione più diffusa sull'argomento.

Che cos'è Chi lo mantiene
OpenAPI lo standard, cioè il formato OpenAPI Initiative, Linux Foundation
Swagger una famiglia di strumenti che leggono quel formato SmartBear

La storia spiega il groviglio. All'inizio Swagger indicava contemporaneamente la specifica e gli strumenti. In seguito la specifica è passata alla OpenAPI Initiative ed è diventata OpenAPI Specification, mentre il nome Swagger è rimasto agli strumenti: Swagger UI, Swagger Editor e gli altri.

Nella pratica, «usiamo Swagger» significa quasi sempre «abbiamo un file OpenAPI e lo mostriamo con Swagger UI». Nulla lega le due cose: lo stesso file si apre altrettanto bene in Redoc, in Scalar o in un generatore di client.

Versioni: 3.0, 3.1 e 3.2

Le differenze fra i rami non sono cosmetiche.

3.0 resta la più diffusa. Il punto debole sono gli schemi dei dati: somigliano a JSON Schema senza essere del tutto compatibili con esso, e questo rompe con regolarità l'interoperabilità fra strumenti.

3.1 ha colmato quella distanza. L'oggetto Schema è diventato un soprainsieme di JSON Schema Draft 2020-12, così gli schemi che già mantenete in JSON Schema si riutilizzano invece di essere riscritti. La revisione in vigore, la 3.1.1, è stata pubblicata il 24 ottobre 2024.

3.2 è arrivata a settembre 2025. Dall'annuncio della OpenAPI Initiative spiccano:

  • il metodo HTTP query, per letture idempotenti i cui parametri non stanno in una URL;
  • additionalOperations, per metodi fuori standard;
  • i tipi di media in streaming: Server-Sent Events, JSON Lines, sequenze JSON;
  • i tag gerarchici, con summary, parent e kind;
  • il flusso per dispositivi di OAuth 2.0, pensato per apparecchi con input scomodo.

Conviene scegliere la versione che i vostri strumenti supportano davvero, non il numero più alto. Il supporto a 3.1 nell'ecosistema è solido; la 3.2 è più recente e una parte della catena di strumenti sta ancora recuperando. La nostra specifica dichiara 3.1.0.

Cosa porta in concreto

Un solo file copre più esigenze insieme:

  • documentazione interattiva — Swagger UI o Redoc costruiscono la pagina direttamente dal file, modulo di prova compreso;
  • librerie client — i generatori producono un SDK nel linguaggio che serve, al posto di wrapper scritti a mano;
  • server simulati — un mock si avvia partendo dalla descrizione prima che il backend esista, e il frontend non resta fermo;
  • test di contratto — i test confrontano le risposte reali con gli schemi e colgono il momento in cui una risposta smette di corrispondere alla sua descrizione.

Da questo elenco nasce l'argomento principale a favore di OpenAPI: la documentazione smette di essere un testo a parte che qualcuno dimentica di aggiornare.

L'argomento è corretto. Semplicemente, non si realizza da sé.

Niente collega la specifica al codice

È la parte che gli articoli introduttivi non toccano quasi mai, ed è quella che morde in produzione.

Il file di specifica è soltanto un file. Il router non sa che esiste. Nemmeno il controller. Potete cancellare un endpoint, aggiungerne uno, rendere facoltativo un campo obbligatorio: non cadrà un solo test. La documentazione non si sincronizza da sola; resta esatta esattamente finché qualcuno si ricorda di modificarla.

Lo scostamento avviene in entrambe le direzioni, e ciascuna fa danno a modo suo:

  • documentato ma non funziona. Chi legge invia la richiesta e riceve un 404. La fiducia nel resto della documentazione crolla all'istante: se qui era falso, dove altro?
  • funziona ma non è documentato. Per chi la cerca, quella funzionalità semplicemente non esiste. Nessuno la trova, nessuno la paga.

Il secondo caso è il più insidioso, perché nessuno protesta. Non c'è nemmeno chi potrebbe farlo: l'utente non sa che quell'endpoint c'è.

È esattamente ciò che è successo a noi. L'API di Lix.li aveva tre endpoint per le conversioni perfettamente funzionanti — un elenco di eventi più la ricezione dei postback, singola e a lotti — e nessuno compariva nella specifica. Il codice girava, i test passavano, e intanto la pagina di presentazione dell'API mostrava due percorsi tutt'altri, che nel codice non esistevano affatto. È emerso durante una verifica completa, non da una segnalazione.

Come intercettare lo scostamento in automatico

Non si risolve con la disciplina ma con un test. L'idea è lineare: prendere l'elenco dei percorsi dalla specifica, prendere l'elenco delle rotte dal router e confrontare i due insiemi in entrambe le direzioni.

percorsi da openapi.yaml   →   la rotta esiste?           →   altrimenti la documentazione mente
rotte dal router           →   il percorso è descritto?   →   altrimenti la funzione è invisibile

La metà «ciò che è documentato funziona» si verifica comodamente con una richiesta reale: chiamare l'endpoint senza credenziali e accertarsi che la risposta sia 401 e non 404. La distinzione conta. 401 vuol dire che la rotta è stata trovata e chiede una chiave; 404, che quel percorso non esiste. Nel frattempo non viene scritto nulla, perché l'autenticazione gira prima del controller.

La verifica inversa legge la configurazione delle rotte. Costringe per giunta a elencare le eccezioni in modo esplicito: gli endpoint interni che in un contratto pubblico non hanno motivo di stare. Quell'elenco vale già di per sé, perché obbliga a decidere una volta, consapevolmente, che cosa considerate la vostra API pubblica.

Abbiamo scritto quel test dopo la vicenda delle conversioni. Si è ripagato subito: se le conversioni vengono tolte di nuovo dalla specifica, il test fallisce e nomina entrambi i percorsi mancanti.

Due trappole che si scoprono tardi

$ref e percorsi relativi. Quando una specifica è divisa in più file, i riferimenti interni si risolvono rispetto alla URL da cui il file è stato servito. Un documento radice disponibile a /openapi andrà a cercare ./paths/links.yaml all'indirizzo /paths/links.yaml e non troverà nulla. Nel browser sembra tutto a posto, perché Swagger UI carica il file dalla sua posizione reale — ma un generatore di client puntato sullo stesso documento inciamperà. O servite la specifica soltanto dalla sua directory, oppure la impacchettate in un file unico senza $ref esterni.

Swagger UI è vuoto per un crawler. Una pagina Swagger UI sono poche righe di markup più uno script che disegna il contenuto nel browser. Nell'HTML servito non compare un solo percorso di endpoint. Per chi legge la pagina va bene; per l'indicizzazione è bianca. Aggiungeteci un errore frequente nel robots.txt: una regola Disallow: /api/, scritta per nascondere gli endpoint JSON, nasconde insieme anche la documentazione per le persone che vive sotto lo stesso prefisso. Va bloccato il percorso esatto con la versione — /api/1.0/, per dire — non l'intera sezione.

Da dove cominciare

Se l'API c'è già e la descrizione no, l'ordine è più o meno questo:

  1. Descrivete un endpoint a mano. Basta quello per afferrare la struttura.
  2. Scegliete la versione che i vostri strumenti reggono senza esitazioni; di base la 3.1.
  3. Dividete il file con $ref quando smette di stare in una schermata, e stabilite subito da quale URL viene servito.
  4. Mettete un test che confronti la specifica con il router nelle due direzioni. Prima di questo passaggio, qualunque affermazione su una documentazione aggiornata è una promessa, non una proprietà.

Per vedere come si presenta su un'API reale, la documentazione dell'API di Lix.li copre link, gruppi, test A/B e conversioni. Il servizio costruito attorno a quell'API è descritto nella pagina dell'API per accorciare link. Due temi vicini meritano un approfondimento a parte: i webhook, se preferite ricevere gli eventi invece di interrogarli, e la differenza fra i redirect 301, 302, 307 e 308 — perché un 301 trasforma un POST in GET, cosa che tocca direttamente gli endpoint di postback citati sopra.