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.
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.