Un'API è un'interfaccia per altri sviluppatori, e come ogni interfaccia può essere intuitiva o frustrante. Un'API ben progettata si capisce leggendo pochi esempi, si comporta in modo prevedibile e fallisce in modo chiaro. Non esiste un'unica forma corretta, ma esistono convenzioni consolidate che rendono la vita più facile a chi la usa e a chi la mantiene.
Pensare per risorse
Nello stile REST, gli URL identificano risorse (cose), mentre i metodi HTTP indicano le azioni. Gli URL contengono quindi nomi, di solito al plurale, e non verbi:
GET /articoli elenco degli articoli
POST /articoli crea un articolo
GET /articoli/42 dettaglio dell'articolo 42
PATCH /articoli/42 modifica parziale
DELETE /articoli/42 eliminazione
GET /articoli/42/commenti commenti dell'articolo 42
Da evitare forme come /getArticoli o /articoli/42/elimina: duplicano nell'URL l'informazione che il metodo già porta.
I metodi HTTP e il loro significato
| Metodo | Uso | Sicuro | Idempotente |
|---|---|---|---|
GET |
leggere | sì | sì |
POST |
creare o avviare un'operazione | no | no |
PUT |
sostituire per intero | no | sì |
PATCH |
modificare in parte | no | non necessariamente |
DELETE |
eliminare | no | sì |
- Sicuro significa che non modifica lo stato del server. Una
GETnon deve mai cancellare o modificare dati: crawler, anteprime dei link e prefetch dei browser eseguono richieste GET in automatico. - Idempotente significa che ripetere la richiesta più volte ha lo stesso effetto di farla una sola volta. È importante perché i client, in caso di errore di rete, tendono a ripetere le richieste.
Per le operazioni POST che non devono essere duplicate, come un pagamento o un ordine, un approccio diffuso è accettare una chiave di idempotenza inviata dal client in un header: se arriva due volte la stessa chiave, il server restituisce il risultato della prima richiesta invece di eseguirla di nuovo.
Codici di stato che dicono la verità
Il codice di stato è la prima cosa che il client controlla. Restituire sempre 200 con un campo "errore": true nel corpo costringe ogni client a interpretare il contenuto e rompe strumenti, cache e monitoraggio.
I codici più utili:
200 OK: richiesta riuscita, con contenuto.201 Created: risorsa creata; includi l'headerLocationcon l'URL della nuova risorsa.204 No Content: riuscita, senza contenuto (tipico di DELETE).400 Bad Request: richiesta malformata.401 Unauthorized: autenticazione mancante o non valida.403 Forbidden: autenticato, ma senza permesso.404 Not Found: la risorsa non esiste.409 Conflict: conflitto con lo stato attuale, per esempio un duplicato.422 Unprocessable Content: sintassi corretta ma dati non validi.429 Too Many Requests: limite di frequenza superato.500 Internal Server Error: errore imprevisto del server.
Errori coerenti e utili
Ogni errore dovrebbe avere la stessa struttura, così il client può gestirli in un unico punto. Esiste anche uno standard IETF per questo scopo, i "Problem Details", con il tipo di contenuto application/problem+json. Un esempio:
{
"type": "https://api.esempio.it/errori/validazione",
"title": "Dati non validi",
"status": 422,
"errors": [
{ "campo": "email", "messaggio": "Formato non valido" }
]
}
Due regole: il messaggio deve aiutare chi integra l'API a capire cosa correggere, e non deve rivelare dettagli interni come query SQL, percorsi dei file o tracce dello stack.
Elenchi: paginazione, filtri e ordinamento
Un elenco non dovrebbe mai restituire tutti gli elementi in un colpo solo. Le due tecniche principali di paginazione:
- Offset e limite:
GET /articoli?limit=20&offset=40. Semplice e permette di saltare a una pagina qualsiasi, ma diventa lenta su tabelle enormi e può saltare o duplicare elementi se i dati cambiano tra una richiesta e l'altra. - Cursore:
GET /articoli?limit=20&dopo=eyJpZCI6MTIzfQ. Il server restituisce un cursore opaco per la pagina successiva. È stabile e efficiente, ma non permette di saltare direttamente alla pagina 37.
{
"dati": [ ... ],
"pagina_successiva": "/articoli?limit=20&dopo=eyJpZCI6MTQzfQ"
}
Filtri e ordinamento si esprimono con parametri di query leggibili: ?stato=pubblicato&ordina=-data. Imposta sempre un limite massimo lato server per limit, per evitare richieste che chiedono un milione di righe.
Versioni
Prima o poi un cambiamento romperà la compatibilità. Le strategie comuni sono la versione nell'URL (/v1/articoli) o in un header. La versione nell'URL è la più semplice da capire e da provare. Qualunque scelta tu faccia:
- aggiungere campi nuovi alle risposte di solito non rompe nulla;
- rimuovere o rinominare campi, o cambiarne il tipo, richiede una nuova versione;
- annuncia con anticipo le dismissioni e mantieni per un periodo ragionevole la versione precedente.
Sicurezza di base
- Solo HTTPS, sempre.
- Autenticazione tramite token inviati nell'header
Authorization, mai nei parametri dell'URL, che finiscono nei log. - Controllo delle autorizzazioni su ogni risorsa: verificare che l'utente sia autenticato non basta, bisogna verificare che l'articolo 42 sia suo. È uno degli errori più comuni nelle API.
- Validazione rigorosa dell'input e limiti di frequenza.
- Risposte che includono solo i campi necessari: non restituire l'intero record del database "per comodità".
Documentazione
Un'API senza documentazione è un'API che nessuno sa usare. Una specifica OpenAPI descrive endpoint, parametri e risposte in un formato leggibile da persone e strumenti, e permette di generare documentazione interattiva e client.
In sintesi
- Usa URL basati su risorse e lascia ai metodi HTTP il compito di indicare l'azione.
- Rispetta sicurezza e idempotenza dei metodi; GET non deve mai modificare dati.
- Restituisci codici di stato veritieri ed errori con struttura coerente e senza dettagli interni.
- Pagina sempre gli elenchi e fissa un limite massimo lato server.
- Verifica le autorizzazioni su ogni singola risorsa e documenta l'API con esempi.