Introduzione: Progettare API è come Costruire Infrastrutture
Se progetti male una REST API, paga il prezzo per anni: clienti confusi, documentazione inesatta, versioning caotico. Ho visto aziende spendere migliaia di ore per aggiustare API progettate male.
Questo articolo condivide le 5 pilastri che ho utilizzato per progettare API che gestiscono milioni di richieste al giorno, mantenendo compatibilità retroattiva e documentazione sempre sincronizzata.
1. Naming Conventions: La Fondazione
La maggior parte degli sviluppatori sottovaluta i nomi. Ecco le regole che uso:
// ✓ CORRETTO: Nomi chiari e consistenti
GET /api/v1/users # Ottieni lista utenti
GET /api/v1/users/{id} # Ottieni un utente
POST /api/v1/users # Crea nuovo utente
PUT /api/v1/users/{id} # Aggiorna utente
DELETE /api/v1/users/{id} # Elimina utente
// ✗ SBAGLIATO: Inconsistente e confuso
GET /api/getUsers # Mismatch con stile
GET /api/user/{id} # Singular vs plural
POST /api/create_user # Ridondante (POST lo dice)
PATCH /api/v1/users/{id} # Quando usare PATCH vs PUT?
Regola d'oro: Usa nomi al plurale per collezioni, singolare per istanze. Nomi descrivono RISORSA, non AZIONE.
2. HTTP Status Code: Risposte Corrette
Molte API restituiscono sempre 200 anche per errori. Questo causa problemi ai client intelligenti che si basano sui status code.
200 OK → Richiesta riuscita
201 Created → Risorsa creata (sempre con Location header)
204 No Content → Richiesta riuscita ma nessun body
400 Bad Request → Errore nel client (validazione fallita)
401 Unauthorized → Autenticazione mancante
403 Forbidden → Autenticato ma non autorizzato
404 Not Found → Risorsa non esiste
409 Conflict → Conflitto (es: duplicate unique key)
429 Too Many Requests → Rate limit exceeded
500 Internal Error → Errore server (log sempre!)
3. Error Response Standardizzata (RFC 7807)
Ogni API dovrebbe rispondere con lo stesso formato di errore:
// Errore API standardizzato
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation Failed",
"status": 400,
"detail": "Email format is invalid",
"instance": "/api/v1/users",
"timestamp": "2026-06-07T10:00:00Z",
"errors": [
{
"field": "email",
"message": "Must be a valid email format",
"value": "not-an-email"
}
]
}
Questo formato permette a frontend e client di gestire errori in modo uniforme, non indovinando dalla response.
4. Versioning: URI vs Header
La scelta più dibattuta. Ecco il mio take:
// Opzione 1: URI versioning (consigliato per breaking changes)
GET /api/v1/users
GET /api/v2/users # Nuovo schema
// Opzione 2: Header versioning (consigliato per minor changes)
GET /api/users
Accept: application/vnd.myapi.v2+json
// Evita: Query string versioning
GET /api/users?api_version=2 # Cattiva pratica
Usa URI versioning (v1, v2) solo quando hai breaking changes. Per aggiungere campi opzionali, basta aggiungere il campo senza cambiare versione. I vecchi client continueranno a funzionare.
5. Autenticazione: JWT, API Key, OAuth2
Ogni approccio ha pro e contro:
- API Key: Semplice, perfetto per servizi server-to-server. NON usare per frontend pubblico.
- JWT: Stateless, scalabile, perfetto per SPA. Ricorda: non è una soluzione di autenticazione, solo di autorizzazione.
- OAuth2: Complesso ma necessario per delegare autenticazione a servizi terzi (Google, GitHub, etc).
// JWT: Access token breve + Refresh token lungo
POST /api/v1/auth/login
Request: { "email": "...", "password": "..." }
Response: {
"access_token": "eyJ...", // 15 min validity
"refresh_token": "xyz...", // 30 days validity
"expires_in": 900
}
// Poi: Header Authorization per ogni richiesta
GET /api/v1/users
Authorization: Bearer eyJ...
6. Documentazione OpenAPI/Swagger
La documentazione è PARTE della API, non aggiunta dopo. Usa OpenAPI 3.0:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: List all users
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Una volta scritto OpenAPI, genera documentazione interattiva con Swagger UI e client SDK automaticamente.
Checklist Final API Design
- ✓ Naming: Plurale per collezioni, singolare per istanze
- ✓ Status code: 2xx per successo, 4xx per client error, 5xx per server error
- ✓ Error response: RFC 7807 format per ogni errore
- ✓ Versioning: URI v1/v2 solo con breaking changes
- ✓ Autenticazione: JWT per frontend, API key per backend
- ✓ Documentazione: OpenAPI 3.0 sempre sincronizzata
Conclusione
Una API ben progettata non è solo codice: è contratto tra cliente e server. Se il contratto è chiaro, tutto il resto è dettaglio.