Vittorio Turiaci

Convenzioni REST API che uso nei miei progetti Spring Boot e ASP.NET Core

Naming, versioning, gestione errori e paginazione: le convenzioni pratiche che applico per API REST coerenti tra Spring Boot e ASP.NET Core.

REST APISpring BootASP.NET Core

Lavorare sia con Spring Boot che con ASP.NET Core mi ha fatto notare quanto le convenzioni contino più del framework: un'API ben progettata si riconosce dalla coerenza, non dallo stack usato per costruirla.

Naming delle risorse

Sostantivi plurali, non verbi: /orders, non /getOrders. Il verbo lo esprime già il metodo HTTP (GET, POST, PUT, DELETE). Le relazioni tra risorse si annidano nell'URL solo quando ha senso semanticamente:

GET  /orders/42/items      → item dell'ordine 42
POST /orders/42/items      → aggiunge un item all'ordine 42
GET  /items/17              → item 17, indipendentemente dall'ordine

Versioning

Preferisco il versioning nell'URL (/api/v1/orders) al versioning tramite header: è visibile, cacheable, e non richiede che chi consuma l'API legga la documentazione per scoprire come specificare la versione. Non è l'unica scelta valida, ma è quella più prevedibile per chi integra l'API da fuori.

Gestione errori coerente

Un errore utile ha una forma prevedibile, indipendentemente dall'endpoint. Uso una struttura simile sia in Spring Boot (via @ControllerAdvice) che in ASP.NET Core (via IExceptionHandler o middleware):

{
  "status": 404,
  "error": "NOT_FOUND",
  "message": "Ordine con id 42 non trovato",
  "path": "/api/v1/orders/42"
}

Il campo error è un codice stabile che il client può usare per la logica applicativa (es. mostrare un messaggio specifico), mentre message è pensato per il debug, non per essere mostrato direttamente all'utente finale.

Paginazione

Per liste potenzialmente grandi, paginazione esplicita fin dal primo giorno — aggiungerla dopo, quando un endpoint è già in produzione e consumato da client esterni, è un breaking change. Uso parametri query page e size, con la risposta che include i metadati:

{
  "data": [ /* ... */ ],
  "page": 0,
  "size": 20,
  "totalElements": 134,
  "totalPages": 7
}

Sia Spring Data (Pageable) che ASP.NET Core hanno primitive pronte per questo pattern: non serve reinventarlo a mano.

Conclusione

Nessuna di queste convenzioni è complicata singolarmente. Il valore sta nell'applicarle in modo coerente su tutti gli endpoint, cosa più facile a dirsi che a farsi quando un'API cresce nel tempo per mano di persone diverse.