f in x
Versioning API: URI, Header e Strategie per Evolvere senza Rompere i Client
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Analisi dei dati e metriche

Versioning API: URI, Header e Strategie per Evolvere senza Rompere i Client

[2026-06-05] Author: Ing. Calogero Bono

Hai mai rilasciato una modifica a un'API e scoperto che i client esistenti si sono rotti? È un classico. Il versioning delle API non è solo una procedura tecnica: è una strategia di convivenza con chi usa il tuo servizio. Ogni volta che aggiungi un campo, cambi un formato, rimuovi un endpoint, rischi di mandare in crash app mobile, integrazioni di terze parti o il tuo stesso frontend.

Noi di Meteora Web lavoriamo quotidianamente con API REST e GraphQL. Abbiamo visto progetti in cui il versioning era assente e il caos regnava. E progetti dove un versioning sbagliato ha moltiplicato la complessità senza risolvere il problema reale. In questa guida operativa ti mostriamo le tre strategie principali — URI versioning, header versioning e altre alternative — con esempi concreti, pro e contro, e la logica per scegliere quella giusta.

Perché fare versioning di un'API

Il versioning dell'API serve a una cosa sola: permettere a te di evolvere il backend senza obbligare i client a cambiare subito. Un client mobile non si aggiorna da solo. Un partner commerciale non riconfigura l'integrazione ogni settimana. La versione dell'API è un contratto: finché quel contratto è valido, il client funziona.

Se non fai versioning, ogni modifica diventa breaking o ti paralizza. Se lo fai male, paghi in complessità e manutenzione. La scelta giusta dipende dal tuo contesto: quanto controllo hai sui client, quanti client ci sono, quanto veloce vuoi evolvere.

Strategia 1: URI Versioning (il più usato, ma attento)

L'URI versioning consiste nell'inserire il numero di versione direttamente nel percorso dell'URL. Esempio classico: /api/v1/users e /api/v2/users.

Come implementarlo

In un framework come Express.js, si definiscono router separati per versione:

const express = require('express');
const app = express();

const v1Router = express.Router();
v1Router.get('/users', (req, res) => { /* logica v1 */ });

const v2Router = express.Router();
v2Router.get('/users', (req, res) => { /* logica v2 */ });

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

app.listen(3000);

In Laravel, puoi organizzare i controller in cartelle V1 e V2 e raggruppare le rotte usando il prefisso:

Route::prefix('api/v1')->group(function () {
    Route::get('/users', [App\Http\Controllers\Api\V1\UserController::class, 'index']);
});

Route::prefix('api/v2')->group(function () {
    Route::get('/users', [App\Http\Controllers\Api\V2\UserController::class, 'index']);
});

Pro

  • Semplicissimo. Il client vede subito la versione nell'URL, nessuna ambiguità.
  • Facile da cache. Proxy e CDN trattano URL diversi come risorse diverse.
  • Esplorabile. Puoi testare versioni diverse direttamente dal browser.

Contro

  • Moltiplica gli URL. Ogni endpoint esiste in ogni versione. Il numero di rotte cresce linearmente.
  • Difficile da deprecare. Una volta creato un path pubblico, toglierlo è un breaking change a sua volta.
  • Incoraggia la pigrizia. È facile creare una nuova versione per ogni piccola modifica, accumulando debito tecnico.

Strategia 2: Header Versioning (più elegante, meno immediato)

Con l'header versioning la versione viene passata tramite un header HTTP, tipicamente Accept o un header custom. Il client specifica la versione quando richiede la risorsa, e il server risponde in base a quella.

Implementazione comune

Usando l'Accept header con media type personalizzato (Content Negotiation):

GET /api/users
Accept: application/vnd.myapi.v1+json

Oppure un header custom come X-API-Version: 1 (meno standard, ma più semplice).

In Express.js, puoi leggere l'header e instradare di conseguenza:

app.get('/api/users', (req, res) => {
  const version = req.headers['accept']?.includes('vnd.myapi.v1') ? 1 : 2;
  if (version === 1) {
    // risposta v1
  } else {
    // risposta v2
  }
});

In Laravel, puoi usare middleware per modificare la response in base all'Accept:

public function handle($request, Closure $next)
{
    $accept = $request->header('Accept');
    if (str_contains($accept, 'vnd.myapi.v1')) {
        // applica trasformazioni v1
    }
    return $next($request);
}

Pro

  • URL pulito. I path rimangono invariati, non si inquinano.
  • Separazione netta. La versione riguarda la rappresentazione, non la risorsa.
  • Meno rotte da mantenere (se gestisci la logica a livello di middleware o trasformazione).

Contro

  • Opaco per i client. Non è immediato capire quale versione si sta usando (bisogna leggere la documentazione).
  • Non funziona facilmente con proxy o CDN che non ispezionano gli header personalizzati.
  • Rischio di ingegneria eccessiva per progetti piccoli. Se hai 2 versioni, l'URI versioning basta e avanza.

Altre strategie: Query Parameter, Media Type e Hybrid

Query Parameter versioning

Aggiungere ?version=1 all'URL. Sembra semplice ma non lo consigliamo. I query parameter sono spesso ignorati da cache, creano ambiguità semantiche (il parametro potrebbe essere parte del filtro dei dati) e sono facili da dimenticare. Usalo solo in ambienti interni e temporanei.

Media type versioning puro

È una variante dell'header versioning dove si usa solo Accept senza modificare l'URL. Il server risponde con il contenuto appropriato. È il modo più RESTful secondo alcuni standard. Però richiede client ben educati e un server che gestisca la negoziazione in modo robusto.

Approccio ibrido

Noi spesso adottiamo una combinazione: URI versioning per le versioni maggiori (breaking changes), e header versioning per versioni minori (aggiunte non breaking). Per esempio: /api/v1/users è la versione 1, ma se aggiungiamo un campo opzionale in risposta, lo segnaliamo con un header X-API-Minor: 2 e il client può decidere se usarlo.

Un'altra strategia che applichiamo nei progetti più grandi è versioning per endpoint: non versionare l'intera API, ma solo gli endpoint che cambiano. Per il resto, si mantiene una compatibilità all'indietro usando i campi opzionali.

Come scegliere? Un metodo pratico

Non esiste una risposta unica. Noi di Meteora Web valutiamo tre fattori:

  1. Numero di client e controllo su di essi. Se hai poche app interne che aggiorni tu, l'URI versioning è overkill – bastano feature flag e compatibilità all'indietro. Se hai migliaia di client esterni, meglio un sistema robusto con header versioning o ibrido.
  2. Frequenza dei cambiamenti. Se rilasci ogni settimana, l'URI versioning ti porterà a centinaia di rotte. In quel caso un header versioning con trasformazioni automatiche è più sostenibile.
  3. Ecosistema di API. Se usi API Gateway (Kong, AWS API Gateway, Apigee), molti di essi supportano nativamente la negoziazione di versione. Approfittane.

Errori comuni che abbiamo visto (e come evitarli)

  • Non deprecare mai le vecchie versioni. Tieni una roadmap: dopo X mesi o Y chiamate, blocca la versione più vecchia. Comunica la scadenza con largo anticipo.
  • Versionare anche i dettagli interni. La versione riguarda il contratto pubblico, non l'implementazione. Non esporre mai l'ID della versione del database.
  • Ignorare la documentazione. Ogni versione deve avere documentazione separata. Swagger/OpenAPI supporta più versioni nello stesso file.

Per approfondire come strutturare un'API in Express.js, dai un'occhiata alla nostra guida su Express.js.

In sintesi — cosa fare adesso

  1. Scegli una strategia primaria: per la maggior parte dei progetti, inizia con URI versioning. È semplice e comprensibile.
  2. Stabilisci una politica di deprecation: dopo 2 versioni attive, la più vecchia va in sunset. Comunica per email e con header Sunset (RFC 8594).
  3. Documenta ogni versione: usa OpenAPI con file separati per v1, v2 ecc. Oppure usa uno strumento come Stoplight.
  4. Testa le versioni vecchie: scrivi test di regressione per ogni versione ancora supportata. Non cancellare mai un endpoint senza averti assicurato che nessuno lo chiami più.
  5. Considera un approccio ibrido se prevedi evoluzioni frequenti: URI per major, header per minor.

Il versioning non è un ornamento – è la differenza tra un'API che evolve in sicurezza e un disastro in produzione. Noi lo vediamo ogni giorno. Se vuoi che il tuo backend cresca senza fare danni, investi in una strategia ora.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()