Che struttura dovrebbe usare una risposta API
In sintesi: Gli oggetti nudi sono puliti finché non ti servono metadati; gli envelope completi sono sicuri ma verbosi. Per la maggior parte dei progetti usa lo stato HTTP per gli esiti e un envelope stabile per i dati.

In sintesi: esprimi gli esiti con codici di stato 4xx/5xx, porta il payload in { "data": ..., "meta": ... }, e passa a un envelope error in caso di fallimento; questo basta per le API interne, e solo le piattaforme pubbliche con molti integratori hanno bisogno di JSON:API o OpenAPI.
Escludi prima l'opzione peggiore: restituire sempre 200 con l'esito in un campo code.
Tre forme
A. Oggetto nudo
{ "id": 1, "name": "demo", "price": 99 }
Pulito, e la semantica HTTP fa il lavoro (404 è 404). Ma nel momento in cui ti servono paginazione, avvisi o un trace id, non c'è posto dove metterli senza cambiare il contratto.
B. Envelope stabile
{
"data": { "id": 1, "name": "demo" },
"meta": { "requestId": "abc", "took": 12 }
}
Payload e metadati vivono separati, quindi i nuovi metadati non rimodellano mai data. Costa un livello di annidamento.
C. Envelope per tutto (da evitare)
{ "code": 0, "message": "success", "data": {...} }
La forma non è il peccato; la pratica lo è. Lo stato 200 per ogni esito significa che CDN, gateway, circuit breaker, politiche di retry e dashboard vedono tutti successo. Ogni alert allora richiede un parsing personalizzato.
La raccomandazione
Stato HTTP per l'esito, envelope per il payload.
{
"data": { "id": 1, "name": "demo" },
"meta": { "requestId": "req_8f2a", "took": 12 }
}
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "Order does not exist or was deleted",
"details": [{ "field": "id", "issue": "not_found" }]
},
"meta": { "requestId": "req_8f2a" }
}
Stato 4xx o 5xx; code stabile e leggibile dalla macchina; message per gli umani; details per gli errori di form a livello di campo.
Regole ferree
- Gli endpoint di elenco devono portare metadati di paginazione. Senza
totalo un cursore non c'è paginatore. - Le liste vuote restituiscono
[], mainull.null.mapè un'interruzione ricorrente. - Timestamp in ISO 8601 con offset. Vedi che formato per i timestamp.
- Una convenzione di denominazione.
snake_caseocamelCase, convertita una volta al confine. - Denaro come unità minori intere o stringhe.
99.99non è esatto nel floating point binario. - Non confondere
nullcon l'assenza. Vedinullo omettere il campo.
Quando adottare una specifica
- Piattaforme pubbliche con molti integratori: JSON:API o OpenAPI più un envelope condiviso
- API interne di un singolo team: l'envelope sopra basta
- Contratti forti: Schema prima, strutture generate da esso
Le specifiche risparmiano discussioni e riducono il costo di onboarding. Per un team di cinque persone, JSON:API è di solito lavoro negativo.
FAQ
Come devo scegliere tra un oggetto nudo e un envelope?
Usa un envelope quando ti servono paginazione, avvisi o un requestId, così meta può crescere senza rimodellare data. Restituisci l'oggetto nudo solo quando non hai bisogno di metadati e puoi accettare che aggiungere campi in seguito cambi il contratto.
Perché un'API non dovrebbe mai restituire 200 per ogni esito?
CDN, gateway, circuit breaker, politiche di retry e dashboard leggono tutti lo stato HTTP per decidere se fare cache, retry o alert. Con 200 ovunque trattano i fallimenti come successi, e ogni alert richiede un parsing personalizzato del codice di business.
Gli endpoint di elenco devono portare metadati di paginazione?
Sì. Senza total o un cursore non c'è paginatore, e le liste vuote devono restituire [] invece di null, altrimenti la chiamata .map del frontend si rompe.