Formati di Risposta API: Best Practice per API Coerenti
Le risposte API incoerenti sono una delle principali lamentele degli sviluppatori frontend. Quando ogni endpoint restituisce dati in una forma diversa, il codice client diventa pieno di casi speciali. Un formato di risposta coerente migliora l'esperienza dello sviluppatore, riduce i bug e rende la tua API auto-documentante.
L'Envelope di Risposta
Avvolgi ogni risposta in una struttura coerente:
Successo (Risorsa Singola)
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
},
"meta": {
"requestId": "req_abc123"
}
}
Successo (Collezione)
{
"data": [
{ "id": "user_123", "name": "Alice" },
{ "id": "user_456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20,
"requestId": "req_def456"
}
}
Errore
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
},
"meta": {
"requestId": "req_ghi789"
}
}
Il principio chiave: i client controllano sempre data o error al livello superiore. Non mescolare mai dati di successo e informazioni di errore nella stessa risposta.
Valida il formato della tua risposta con il nostro Validatore JSON.
Codici di Stato HTTP
Usa i codici di stato correttamente — sono la prima cosa che i client controllano:
Codici di Successo
| Codice | Quando |
|---|---|
| 200 OK | GET riuscito, PUT/PATCH riuscito |
| 201 Created | POST che ha creato una risorsa |
| 204 No Content | DELETE riuscito (nessun body) |
Codici di Errore Client
| Codice | Quando |
|---|---|
| 400 Bad Request | Sintassi della richiesta malformata |
| 401 Unauthorized | Autenticazione mancante o non valida |
| 403 Forbidden | Autenticato ma non autorizzato |
| 404 Not Found | La risorsa non esiste |
| 409 Conflict | Conflitto di stato della risorsa (duplicato) |
| 422 Unprocessable | Sintassi valida ma errori semantici |
| 429 Too Many Requests | Limite di frequenza superato |
Codici di Errore Server
| Codice | Quando |
|---|---|
| 500 Internal Server Error | Errore server inatteso |
| 502 Bad Gateway | Errore servizio upstream |
| 503 Service Unavailable | Sovraccarico temporaneo o manutenzione |
Design della Risposta di Errore
Buone risposte di errore aiutano gli sviluppatori a fare debug rapidamente:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 'user_999' not found",
"details": [],
"documentationUrl": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
},
"meta": {
"requestId": "req_xyz789",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Regole:
codeè leggibile dalla macchina (stringa costante, non stato HTTP)messageè leggibile dall'uomo (può cambiare senza rompere i client)detailsfornisce errori a livello di campo per i fallimenti di validazionerequestIdpermette al team di supporto di tracciare la richiesta nei log
Pattern di Paginazione
Basata su Offset
Semplice e supporta il salto a pagine arbitrarie:
{
"data": ["..."],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
},
"links": {
"first": "/api/users?page=1&per_page=20",
"prev": "/api/users?page=1&per_page=20",
"next": "/api/users?page=3&per_page=20",
"last": "/api/users?page=8&per_page=20"
}
}
Basata su Cursore
Migliore per dati in tempo reale e grandi dataset:
{
"data": ["..."],
"meta": {
"hasNext": true,
"hasPrev": true
},
"links": {
"next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
"prev": "/api/users?cursor=eyJpZCI6MTIzfQ&limit=20&direction=prev"
}
}
Data e Ora
Usa sempre ISO 8601 con informazione sul fuso orario:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33+00:00",
"expiresAt": "2024-12-31T23:59:59Z"
}
Non usare mai:
- Timestamp Unix (ambigui: secondi o millisecondi?)
- Date locali senza fuso orario
- Formati personalizzati come MM/GG/AAAA
Per approfondire la gestione dei timestamp, consulta la nostra guida ai Timestamp Unix.
Null vs Campi Assenti
Due approcci comuni:
Includi con null (esplicito):
{ "name": "Alice", "avatar": null, "bio": null }
Ometti i campi assenti (sparso):
{ "name": "Alice" }
Raccomandazione: Sii coerente all'interno della tua API. I null espliciti sono migliori per i linguaggi tipizzati (i client sanno che il campo esiste). Lo stile sparso è migliore per ambienti con larghezza di banda limitata.
Versionamento delle Risposte
Quando il formato della risposta cambia, versiona la tua API:
GET /api/v2/users/123
Modifiche che richiedono versionamento:
- Rimuovere un campo
- Cambiare il tipo di un campo
- Rinominare un campo
- Cambiare la struttura dell'envelope di risposta
Modifiche non distruttive (sicure senza versionamento):
- Aggiungere nuovi campi
- Aggiungere nuovi endpoint
- Aggiungere nuovi valori enum
FAQ
Dovrei avvolgere le risposte di successo in una chiave data o restituire la risorsa direttamente?
Usare un wrapper data fornisce una struttura coerente per tutte le risposte e lascia spazio per metadati, paginazione e link insieme ai dati. Restituire la risorsa direttamente è più semplice per endpoint a risorsa singola. La maggior parte delle API moderne usa l'approccio wrapper per coerenza.
Come dovrei gestire i fallimenti parziali nelle operazioni batch?
Restituisci 200 con una risposta che include sia successi che fallimenti nell'oggetto data. Usare 207 Multi-Status (WebDAV) è un'altra opzione ma meno comunemente usata nelle API REST.
Risorse Correlate
- Formattatore JSON — Formatta le risposte API
- Pattern di Design per API JSON — Guida completa al design delle API
- Validazione JSON Schema — Valida le strutture delle risposte