alltools.one
API
2025-06-30
9 min
alltools.one Team
JSONAPIRESTDesign PatternsBackend

Pattern di Design per API JSON: Costruire API REST Migliori

Un'API ben progettata è un piacere da usare. Una progettata male crea bug, frustrazione e debito tecnico. Questa guida copre pattern collaudati per progettare API REST JSON che siano coerenti, scopribili e mantenibili.

Denominazione delle Risorse

Le risorse sono sostantivi, non verbi. Usa sostantivi plurali per le collezioni e risorse singole tramite ID:

GET    /api/users          # Lista utenti
POST   /api/users          # Crea un utente
GET    /api/users/123      # Ottieni utente 123
PUT    /api/users/123      # Aggiorna utente 123
DELETE /api/users/123      # Elimina utente 123

Convenzioni di denominazione:

  • Usa minuscole con trattini: /api/blog-posts (non blogPosts o blog_posts)
  • Annida risorse correlate: /api/users/123/orders
  • Limita l'annidamento a 2 livelli: /api/users/123/orders/456 (non più in profondità)
  • Evita verbi negli URL: /api/users/123/activate è accettabile per azioni che non mappano a CRUD

Envelope di Risposta

Avvolgi le risposte in un envelope coerente:

{
  "data": {
    "id": "123",
    "type": "user",
    "attributes": {
      "name": "Alice",
      "email": "alice@example.com",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  },
  "meta": {
    "requestId": "req_abc123"
  }
}

Per le collezioni:

{
  "data": [
    { "id": "123", "name": "Alice" },
    { "id": "456", "name": "Bob" }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "perPage": 20
  }
}

Valida le risposte della tua API con il nostro Validatore JSON per assicurarti che corrispondano al tuo schema.

Paginazione

Tre approcci comuni:

Basata su Offset (Più Semplice)

GET /api/users?page=2&per_page=20
{
  "data": [...],
  "meta": {
    "page": 2,
    "perPage": 20,
    "total": 142,
    "totalPages": 8
  }
}

Pro: Semplice, supporta il salto a qualsiasi pagina. Contro: Risultati incoerenti con inserimenti/eliminazioni concorrenti.

Basata su Cursore (Più Affidabile)

GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
  "data": [...],
  "meta": {
    "hasNext": true,
    "nextCursor": "eyJpZCI6MTQzfQ"
  }
}

Pro: Coerente con modifiche concorrenti, performante su grandi dataset. Contro: Non può saltare a pagine arbitrarie.

Basata su Keyset (Più Performante)

GET /api/users?after_id=123&limit=20

Usa l'ID dell'ultimo elemento (o altro campo ordinato) per ottenere la pagina successiva. Simile alla paginazione basata su cursore ma con parametri trasparenti.

Raccomandazione: Usa basata su cursore per feed in tempo reale e grandi dataset. Usa basata su offset per dashboard admin dove il salto di pagina è importante.

Filtraggio e Ordinamento

Filtraggio

GET /api/users?status=active&role=admin
GET /api/users?created_after=2024-01-01
GET /api/users?search=alice

Per filtri complessi, considera un parametro di query dedicato:

GET /api/users?filter[status]=active&filter[role]=admin

Ordinamento

GET /api/users?sort=name          # Ascendente
GET /api/users?sort=-created_at   # Discendente (prefisso con -)
GET /api/users?sort=-created_at,name  # Campi multipli

Gestione degli Errori

Risposte di errore coerenti sono critiche per l'usabilità dell'API:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "value": "not-an-email"
      },
      {
        "field": "age",
        "message": "Must be between 0 and 150",
        "value": -5
      }
    ]
  },
  "meta": {
    "requestId": "req_abc123"
  }
}

Codici di stato HTTP da usare:

CodiceSignificatoQuando
200OKGET, PUT riusciti
201CreatedPOST che ha creato una risorsa
204No ContentDELETE riuscito
400Bad RequestErrori di validazione
401UnauthorizedAuth mancante/non valida
403ForbiddenAuth valida, permessi insufficienti
404Not FoundLa risorsa non esiste
409ConflictRisorsa duplicata, conflitto di versione
422UnprocessableSemanticamente non valido
429Too Many RequestsLimite di frequenza superato
500Internal ErrorErrore server inatteso

Versionamento

Tre approcci:

Percorso URL (Raccomandato)

GET /api/v1/users
GET /api/v2/users

Pro: Esplicito, facile da routare, facile da testare.

Basato su Header

GET /api/users
Accept: application/vnd.myapi.v2+json

Pro: URL puliti. Contro: Più difficile da testare, meno scopribile.

Parametro Query

GET /api/users?version=2

Pro: Facile da testare. Contro: Semantica dei parametri opzionali.

Raccomandazione: Il versionamento nel percorso URL è la scelta più pratica. È esplicito, cacheable e funziona con ogni strumento HTTP.

Gestione di Date e Orari

Usa sempre ISO 8601 in UTC:

{
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T14:22:33Z",
  "expiresAt": "2024-12-31T23:59:59Z"
}

Non usare mai timestamp Unix nelle risposte API — sono ambigui (secondi vs millisecondi) e non leggibili dall'uomo. Per approfondire la gestione dei timestamp, consulta la nostra guida ai Timestamp Unix.

Rate Limiting

Comunica i limiti di frequenza negli header di risposta:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1705312800
Retry-After: 30

Restituisci 429 Too Many Requests quando il limite viene superato, con un messaggio di errore chiaro e header Retry-After.

HATEOAS (Opzionale ma Potente)

Includi link a risorse e azioni correlate:

{
  "data": {
    "id": "123",
    "name": "Alice",
    "links": {
      "self": "/api/users/123",
      "orders": "/api/users/123/orders",
      "avatar": "/api/users/123/avatar"
    }
  }
}

Questo rende la tua API auto-documentante e riduce la costruzione di URL lato client.

FAQ

Dovrei usare la specifica JSON:API o progettare il mio formato?

La specifica JSON:API (jsonapi.org) fornisce uno standard completo, ma può essere verbosa per API semplici. Per la maggior parte dei progetti, progettare un formato personalizzato più semplice seguendo i pattern in questa guida è più pratico. Usa JSON:API se hai bisogno di librerie client automatiche e uno standard rigoroso.

Come dovrei gestire gli aggiornamenti parziali (PATCH vs PUT)?

Usa PUT per la sostituzione completa della risorsa (il client invia tutti i campi). Usa PATCH per aggiornamenti parziali (il client invia solo i campi modificati). PATCH con JSON Merge Patch (RFC 7396) è l'approccio più semplice: invia un oggetto JSON con solo i campi da aggiornare, e null per rimuovere un campo.

Risorse Correlate

Published on 2025-06-30
JSON API Design Patterns: Building Better REST APIs | alltools.one