Salta al contenuto principale

Buone pratiche

Usa i metodi HTTP secondo la loro semantica definita

Mappa le operazioni sul metodo la cui semantica corrisponde: GET per il recupero safe, PUT e DELETE per scritture idempotenti, POST per creazioni non idempotenti, PATCH per aggiornamenti parziali. Rispettare questi contratti permette a cache, proxy e client di ragionare sulle richieste e di ritentarle in sicurezza.

  • Fai: POST /orders per creare, poi GET /orders/{id} per rileggerlo.
  • Non fare: usare GET /orders/{id}/delete per mutare lo stato - un metodo safe non deve mai cambiare nulla.

Restituisci lo status code semanticamente corretto

Usa lo status code come segnale primario di successo/fallimento: 201 per creato, 204 per nessun contenuto, 400/422 per input non valido, 401/403 per l'auth, 404 per mancante, 409 per conflitto, 429 per i rate limit, 5xx per i guasti del server. Client e intermediari generici dipendono dal codice, non dal parsing del testo.

  • Fai: restituire 409 Conflict quando viene violato un vincolo di unicità in creazione.
  • Non fare: restituire 200 OK con {"error": "..."} nel body - nasconde il fallimento a ogni strumento.

Standardizza i body di errore con Problem Details

Adotta RFC 9457 application/problem+json così ogni errore ha la stessa forma type, title, status, detail e instance più campi di estensione stabili. Un contratto di errore uniforme permette a un solo percorso di codice client di gestire i fallimenti di ogni endpoint.

  • Fai: restituire {"type": "https://errors.example.com/out-of-stock", "title": "Out of stock", "status": 409, "detail": "SKU-42 ha 0 unità"}.
  • Non fare: inventare una forma JSON di errore ad hoc diversa per ogni endpoint.

Versiona l'API e rompi solo in una nuova major

Scegli uno schema di versioning (URI come /v1, o media type) e mantieni le modifiche dentro una versione major retrocompatibili: aggiungi campi ed endpoint opzionali, non rimuovere o rinominare mai. I client scritti su una versione major devono continuare a funzionare finché non la ritiri deliberatamente.

  • Fai: aggiungere un nuovo campo opzionale currency a una risposta dentro v1.
  • Non fare: cambiare amount da numero a oggetto dentro v1 - rompe ogni parser esistente.

Comunica la deprecazione prima della rimozione

Quando un endpoint o un campo sta per sparire, segnalalo per tempo con gli header Deprecation e Sunset (RFC 9745, RFC 8594), pubblica una guida di migrazione e mantieni il vecchio comportamento per una finestra dichiarata. Rimuovere qualcosa che i client chiamano ancora, senza preavviso, è il modo più rapido per rompere le integrazioni.

  • Fai: restituire Deprecation: true e Sunset: Wed, 31 Dec 2026 23:59:59 GMT più un Link alla guida di migrazione.
  • Non fare: cancellare in silenzio /v1/legacy-report e lasciare che i chiamanti lo scoprano dai 404.

Rendi ripetibili in sicurezza le operazioni non safe con chiavi di idempotenza

Le reti perdono le risposte, quindi i client ritentano; assicurati che una creazione ripetuta non agisca due volte accettando una Idempotency-Key fornita dal client. Memorizza la chiave con la sua prima risposta per una finestra limitata e restituisci quella risposta in caso di ripetizione.

  • Fai: accettare Idempotency-Key: su POST /payments e deduplicare su di essa.
  • Non fare: assumere che il client non re-invii mai un POST - lo farà, dopo qualsiasi timeout.

Per default usa la paginazione a cursore per le collezioni

Restituisci le collezioni in pagine limitate e preferisci la paginazione a cursore (keyset) così i risultati restano corretti e veloci anche mentre i dati cambiano. Limita sempre la dimensione massima di pagina ed esponi il cursore o i link successivi in modo coerente.

  • Fai: rispondere con {"data": [...], "next_cursor": "eyJpZCI6..."} e accettare ?after=&limit=20.
  • Non fare: restituire un array illimitato di ogni riga, o usare offset grandi che fanno table-scan.

Valida e limita tutto l'input al confine

Tratta ogni richiesta come non fidata: valida tipi, intervalli, lunghezze e valori enum prima di elaborare, e rifiuta rapidamente l'input malformato con un chiaro 400/422. Limita dimensioni di array, lunghezze di stringa e dimensioni di pagina così una sola richiesta non possa esaurire le risorse.

  • Fai: rifiutare limit=100000 con 400 e un messaggio che indica il massimo consentito.
  • Non fare: passare input grezzo del client direttamente in una query o in una chiamata a valle.

Autentica e autorizza a ogni richiesta, lato server

Poiché REST è stateless, ogni richiesta deve portare le credenziali, e il server deve verificarle ogni volta - valida firma, exp, iss e aud di un bearer token - poi autorizza separatamente l'azione specifica sulla risorsa specifica. Non fidarti mai di un ruolo o ID asserito dal client.

  • Fai: verificare che token.sub possieda order/{id} prima di restituirlo, rispondendo 403 altrimenti.
  • Non fare: affidarti al client che nasconde un pulsante, o a un flag is_admin inviato dal client.

Servi solo su TLS

Esponi l'API esclusivamente su HTTPS così credenziali, token e payload non possano essere intercettati o manomessi, e rifiuta l'HTTP in chiaro. Richiedi TLS moderno (1.2+) e valuta HSTS per prevenire il downgrade.

  • Fai: reindirizzare o rifiutare http:// e inviare Strict-Transport-Security.
  • Non fare: accettare token su una connessione non cifrata "solo per test" in produzione.

Applica i rate limit e mostrali negli header

Proteggi il servizio con rate limit per-client, rifiuta l'eccesso con 429 e rendi i limiti visibili tramite RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset e Retry-After. Limiti prevedibili permettono ai client ben educati di auto-limitarsi invece di essere colti di sorpresa.

  • Fai: restituire 429 con Retry-After: 30 quando il bucket è vuoto.
  • Non fare: scartare in silenzio o lasciare appese le richieste oltre il limite, lasciando i client a indovinare.

Progetta API-first da un contratto scritto

Scrivi l'OpenAPI (o lo schema .proto/GraphQL) prima di implementare, rivedilo con i consumatori e genera documentazione, mock, SDK e contract test da esso. Il contratto diventa la fonte unica di verità e permette ai team client e server di lavorare in parallelo.

  • Fai: tenere openapi.yaml sotto version control e far fallire la CI se l'implementazione diverge da esso.
  • Non fare: scrivere a mano una documentazione a posteriori che lentamente diverge da ciò che il codice restituisce davvero.

Mantieni i nomi delle risorse coerenti e basati su sostantivi

Usa sostantivi plurali per le collezioni, una sola convenzione di casing e i verbi HTTP (non verbi nel path) per le azioni, ed evita di annidare più di uno o due livelli. La coerenza rende l'API indovinabile e riduce la documentazione che un client deve leggere.

  • Fai: GET /customers/{id}/orders e GET /orders?customerId={id} in modo coerente.
  • Non fare: mescolare /getCustomer, /Customers/ e /customer_list nella stessa API.

Usa gli ETag per caching e controllo di concorrenza

Restituisci un ETag per le risorse cacheable così i client possono rivalidare con If-None-Match (ottenendo un 304 economico) e aggiornare in sicurezza con If-Match, che rifiuta le scritture obsolete con 412. Questo riduce la banda e previene gli aggiornamenti persi senza lock lato server.

  • Fai: emettere ETag: "v7" e rifiutare un PUT il cui If-Match non corrisponde alla versione corrente.
  • Non fare: lasciare che due aggiornamenti concorrenti sovrascrivano in silenzio le rispettive modifiche.