API Design: REST vs GraphQL vs gRPC — Come Scegliere
REST, GraphQL e gRPC risolvono ciascuno un problema diverso. Questa guida confronta le loro filosofie di design, compromessi e casi d'uso ideali in modo che tu possa prendere una decisione informata per la tua prossima API.
Ogni applicazione moderna comunica con altre applicazioni attraverso API. La domanda non e' piu' se costruire un'API, ma quale stile di API costruire. REST e' stato il default per oltre un decennio. GraphQL e' emerso come risposta all'inflessibilita' di REST. gRPC adotta un approccio completamente diverso basato su HTTP/2 e protocol buffer. Scegliere tra di essi richiede di comprendere non solo la sintassi, ma la filosofia di design sottostante che ciascuno rappresenta.
Questa guida analizza ogni protocollo da una prospettiva di design: come strutturi risorse o schemi, come i client interagiscono con la tua API, come gestisci versioning e paginazione e come i requisiti in tempo reale influenzano la decisione. Alla fine, avrai un quadro per scegliere lo strumento giusto per i tuoi vincoli specifici.
REST: Design Orientato alle Risorse Che il Web Comprende
REST — Representational State Transfer — non e' un protocollo. E' uno stile architetturale definito da Roy Fielding nella sua tesi di dottorato del 2000. L'idea centrale e' che modelli il tuo dominio come risorse, ciascuna identificata da un URL, e manipoli quelle risorse attraverso un insieme uniforme di verbi HTTP: GET, POST, PUT, PATCH e DELETE.
Un buon design REST e' un design orientato alle risorse. Non progetti endpoint intorno ad azioni — progetti endpoint intorno a sostantivi. Invece di /createUser o /getUserById, progetti POST /users e GET /users/:id. Sembra una distinzione piccola, ma influisce profondamente su come l'API scala, come viene documentata e come i client imparano ad usarla.
Un endpoint REST ben progettato per una risorsa utente si presenta cosi':
GET /users/42
Accept: application/json
Response 200:
{
"id": 42,
"name": "Ada Lovelace",
"email": "ada@example.com",
"role": "admin",
"createdAt": "2025-11-14T09:00:00Z"
}La bellezza di REST e' la sua semplicita'. L'URL identifica la risorsa, il metodo HTTP identifica l'operazione e il corpo della risposta contiene la rappresentazione. Ogni client HTTP in ogni lingua gia' comprende questo contratto. Il caching funziona subito attraverso le intestazioni HTTP. Strumenti come OpenAPI (ex Swagger) ti permettono di generare documentazione, SDK client e stub server da un singolo file schema.
REST incontra problemi quando i client hanno bisogno di forme diverse di dati. Un client mobile potrebbe aver bisogno solo del nome e del ruolo dell'utente, mentre un client dashboard ha bisogno del profilo completo con dati di organizzazione annidati. Con REST, o costruisci endpoint multipli o costringi ogni client a scaricare la rappresentazione completa e scartare cio' che non serve. Questo e' il problema dell'over-fetching, ed e' stata la motivazione principale per GraphQL.
GraphQL: Lascia che il Client Descriva Esattamente Cosa Serve
GraphQL, sviluppato da Meta e rilasciato nel 2015, adotta l'approccio opposto. Invece che il server definisca risposte fisse, il client invia una query che descrive esattamente i dati che vuole, e il server restituisce esattamente quella forma. Un endpoint, qualsiasi forma di risposta.
Uno schema GraphQL definisce i tipi e le relazioni nel tuo dominio. Il client compone query contro questo schema, richiedendo solo i campi di cui ha bisogno. Lo stesso concetto di risorse annidate di REST diventa campi annidati in una query GraphQL, ma il client controlla la profondita' e la selezione.
// GraphQL schema (SDL)
type User {
id: ID!
name: String!
email: String!
role: Role!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
}
enum Role { USER ADMIN MODERATOR }
type Query {
user(id: ID!): User
}Un client che ha bisogno solo del nome dell'utente e dei titoli dei post invia una query che chiede esattamente quei campi:
// Client query
query {
user(id: 42) {
name
posts {
title
}
}
}
// Response
{
"data": {
"user": {
"name": "Ada Lovelace",
"posts": [
{ "title": "Notes on the Analytical Engine" }
]
}
}
}Il principio chiave del design in GraphQL e' che lo schema e' il contratto. Lo schema definisce cosa e' possibile, e il client decide cosa richiedere dalle opzioni disponibili. Questo elimina l'over-fetching, riduce il numero di richieste di rete (una singola query puo' sostituire multiple interazioni REST) e da' ai team frontend indipendenza dai cambiamenti backend perche' nuovi requisiti client non richiedono sempre nuovi endpoint.
GraphQL introduce complessita' propria. Le performance delle query sono piu' difficili da prevedere perche' il client controlla cosa si carica. Il problema N+1 — dove risolvere campi annidati innesca una query al database indipendente per ogni record padre — richiede soluzioni di batching come DataLoader. Il caching a livello HTTP non funziona perche' ogni query colpisce lo stesso endpoint POST, quindi sono necessarie strategie di caching a livello applicativo. E la curva di apprendimento per resolver, mutazioni, subscription e tipi di input e' piu' ripida del modello diretto URL-e-verbo di REST.
GraphQL da' ai team frontend indipendenza dai cambiamenti backend. Ma quella indipendenza comporta la responsabilita' di comprendere le caratteristiche di performance di ogni query che il tuo client invia al server.
gRPC: RPC ad Alte Performance Basato su HTTP/2 e Protobuf
gRPC, originariamente sviluppato da Google, adotta un approccio ancora diverso. Invece di risorse e query, gRPC modella le API come chiamate a procedure remote — definisci un servizio con metodi, e i client chiamano quei metodi come se fossero funzioni locali. La differenza tecnica chiave e' che gRPC usa Protocol Buffer (protobuf) per la serializzazione invece di JSON, e HTTP/2 per il trasporto invece di HTTP/1.1.
Una definizione di servizio protobuf non assomiglia affatto a un endpoint REST o a uno schema GraphQL:
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
rpc CreateUser (CreateUserRequest) returns (User);
rpc StreamUserUpdates (StreamRequest) returns (stream UserUpdate);
}
message GetUserRequest {
int32 id = 1;
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
Role role = 4;
string created_at = 5;
}
enum Role { ADMIN = 0; USER = 1; }
message StreamRequest {}
message UserUpdate {
User user = 1;
string event_type = 2;
}Protobuf serializza in un formato binario compatto che e' significativamente piu' piccolo di JSON e piu' veloce da parsare. Combinato con il multiplexing di HTTP/2 — flussi multipli su una singola connessione — gRPC ottiene una latenza drammaticamente inferiore e un throughput piu' alto rispetto a REST o GraphQL per comunicazioni ad alto volume. Questo rende gRPC la scelta dominante per la comunicazione tra microservizi, dove ogni millisecondo di overhead di serializzazione e ogni byte sul filo contano.
gRPC supporta anche nativamente quattro tipi di streaming: unario (una richiesta, una risposta), server-streaming (una richiesta, flusso di risposte), client-streaming (flusso di richieste, una risposta) e streaming bidirezionale (entrambi i lati trasmettono simultaneamente). Questo lo rende l'opzione piu' forte per le API in tempo reale tra i tre protocolli.
Il compromesso e' che gRPC e' piu' difficile da usare dai browser. I client browser non possono accedere ai servizi gRPC direttamente perche' mancano di controllo granulare sui frame HTTP/2. gRPC-Web esiste come workaround ma aggiunge complessita'. I messaggi Protobuf non sono leggibili dall'uomo in transito, il che rende il debugging piu' difficile — hai bisogno di strumenti come grpcurl o un servizio di reflection per ispezionare il traffico. E l'ecosistema per la documentazione API e' meno maturo di OpenAPI o del sistema di introspezione di GraphQL.
Come si Confrontano su Aspetti Pratici
Scegliere tra REST, GraphQL e gRPC e' raramente una decisione puramente tecnica. La scelta giusta dipende da chi sono i tuoi client, che tipo di dati servono e dai vincoli della tua rete e infrastruttura. Ecco come ogni protocollo si confronta sugli aspetti pratici che contano di piu' in produzione.
Versioning delle API
REST gestisce il versioning attraverso l'URL o l'intestazione Accept. /v1/users e /v2/users possono coesistere indefinitamente, permettendo ai client di migrare al proprio ritmo. Questo e' semplice e collaudato, ma incoraggia la duplicazione — l'endpoint v2 spesso duplica la maggior parte della logica di v1 con poche modifiche. GraphQL evita completamente il versioning evolvendo lo schema. I campi deprecati sono decorati con una direttiva @deprecated e rimangono nello schema finche' ogni client non ha migrato. Questo funziona bene quando controlli tutti i client ma puo' causare attrito nelle API pubbliche dove i client si aggiornano lentamente. Lo schema protobuf di gRPC e' forward-compatibile per design — puoi aggiungere campi senza rompere i client esistenti, e i campi deprecati vengono rimossi dopo un periodo di migrazione. Questa e' la strategia di versioning piu' elegante dei tre, ma richiede disciplina per non rinominare o rimuovere mai un campo senza un ciclo di deprecazione.
Pattern di Paginazione
La paginazione REST e' tipicamente fatta con paginazione basata su cursore usando un campo nextCursor, o paginazione basata su offset con parametri page e limit. Il principio di design e' che la risposta contiene un link o token successivo, e il client lo segue. La paginazione GraphQL usa la specifica Relay Connection, che avvolge le liste in bordi con cursori e informazioni di pagina — piu' verbosa ma piu' consistente degli approcci REST ad-hoc. La paginazione gRPC e' gestita manualmente nei messaggi di richiesta e risposta, di solito con un campo page_token e page_size. Non c'e' uno standard, ma lo schema protobuf rende il contratto esplicito.
- REST: la paginazione basata su cursore con nextCursor nella risposta e' il pattern raccomandato per le API di produzione
- GraphQL: la specifica Relay Connection fornisce un modello di paginazione standardizzato con hasNextPage, hasPreviousPage e cursori
- gRPC: la paginazione e' definita nei tuoi messaggi proto; un pattern comune e' includere page_token e page_size nella richiesta e next_page_token nella risposta
Autenticazione e Autorizzazione
Tutti e tre i protocolli si basano sulla stessa sicurezza di trasporto sottostante. REST usa tipicamente token Bearer nell'intestazione Authorization. GraphQL segue lo stesso pattern — poiche' tutte le richieste passano attraverso un singolo endpoint POST, il token viene validato al livello GraphQL o in un middleware. gRPC usa intercettori per allegare metadati di autenticazione a ogni chiamata, di solito sotto forma di token JWT trasportati nelle intestazioni dei metadati gRPC. Nessuno dei tre protocolli prescrive un metodo di autenticazione specifico — la scelta di OAuth2, chiavi API o autenticazione basata su sessione e' ortogonale allo stile API.
Rate Limiting
Il rate limiting e' piu' semplice con REST perche' ogni endpoint rappresenta un'operazione delimitata. Conti le richieste per endpoint per client e restituisci 429 Too Many Requests quando il limite viene superato. GraphQL rende il rate limiting piu' difficile perche' una singola query puo' innescare quantita' di lavoro vastly diverse — una query per un campo potrebbe costare una lookup al database, mentre una query con relazioni profondamente annidate potrebbe costarne cinquanta. Le API GraphQL tipicamente implementano il rate limiting basato sul costo, dove ogni campo ha un peso e il costo totale della query viene calcolato prima dell'esecuzione. Il rate limiting gRPC e' simile a REST ma opera a livello di metodo — conti le chiamate RPC per metodo per client, con la considerazione aggiuntiva che le chiamate in streaming consumano risorse per la loro durata, non solo per la loro attivazione.
API in Tempo Reale e Streaming
REST puo' gestire aggiornamenti in tempo reale attraverso WebSocket o Server-Sent Events (SSE), ma nessuno dei due fa parte della specifica REST — sono protocolli separati aggiunti accanto all'API HTTP. GraphQL ha le subscription, che sono una parte di prima classe della specifica. Un client si sottoscrive a un evento e riceve aggiornamenti attraverso una connessione WebSocket ogni volta che l'evento si verifica. gRPC ha il supporto piu' forte per il tempo reale con il suo streaming bidirezionale nativo su HTTP/2. Una singola chiamata gRPC puo' trasmettere dati in entrambe le direzioni simultaneamente, che e' ideale per dashboard in tempo reale, applicazioni di chat e sistemi guidati dagli eventi. Se la comunicazione in tempo reale e' un requisito primario, lo streaming di gRPC e' l'opzione piu' matura e performante, seguita dalle subscription GraphQL, seguite da REST piu' WebSocket.
Documentazione ed Esperienza Sviluppatore
REST ha l'ecosistema di documentazione piu' forte grazie a OpenAPI (ex Swagger). Una specifica OpenAPI descrive ogni endpoint, parametro di richiesta, schema di risposta e metodo di autenticazione in un file YAML o JSON leggibile dalla macchina. Strumenti come Swagger UI lo rendono in una pagina di documentazione interattiva, e generatori di codice producono SDK client per dozzine di linguaggi. La maturita' di questo ecosistema significa che un nuovo sviluppatore puo' passare dalla lettura di una specifica OpenAPI alla sua prima chiamata API di successo in minuti.
GraphQL ha l'introspezione — un sistema integrato dove interroghi lo schema dell'API attraverso un endpoint speciale. Strumenti come GraphiQL e Apollo Studio Explorer permettono agli sviluppatori di navigare lo schema, scrivere query con autocompletamento e vedere la documentazione inline. Questa e' un'ottima esperienza per gli sviluppatori che sanno gia' che la tua API esiste, ma non aiuta con la scopribilita' nei motori di ricerca o nei marketplace di API esterne come fa una specifica OpenAPI.
gRPC si basa sui file proto come fonte di verita'. Il file proto e' sia lo schema che la documentazione. Strumenti come protoc-gen-doc generano documentazione di riferimento dai file proto, e la reflection gRPC permette ai client di scoprire i servizi dinamicamente. L'esperienza sviluppatore per gRPC sta migliorando ma e' ancora indietro rispetto a REST e GraphQL in termini di maturita' degli strumenti, specialmente al di fuori dell'ecosistema dei microservizi.
Quando Usare Ciascuno
REST e' la scelta giusta quando la tua API e' consumata da sviluppatori esterni, quando hai bisogno della massima interoperabilita' e quando i tuoi client hanno bisogno di caching HTTP standard. E' il default piu' sicuro perche' ogni linguaggio, framework e proxy comprende HTTP. Se stai costruendo un'API pubblica, inizia con REST.
GraphQL e' la scelta giusta quando la tua API ha tipi di client multipli con requisiti di dati diversi, quando i team frontend hanno bisogno di muoversi indipendentemente dai cambiamenti backend e quando vuoi ridurre il numero di richieste di rete. Eccelle nelle applicazioni rivolte ai consumatori con client mobile e web che hanno bisogno di forme diverse degli stessi dati.
gRPC e' la scelta giusta per la comunicazione interna tra microservizi, per sistemi ad alto throughput dove l'overhead di serializzazione conta e per applicazioni di streaming in tempo reale. Se stai costruendo un sistema dove entrambe le estremita' sono sotto il tuo controllo e le performance sono la preoccupazione principale, gRPC e' l'opzione piu' forte.
Nessuno di questi protocolli e' mutuamente esclusivo. Un'architettura di produzione comune usa gRPC per la comunicazione servizio-a-servizio dietro il firewall, espone un gateway GraphQL che aggrega dati da piu' servizi gRPC e fornisce un'API REST per i client esterni. La domanda non e' quale protocollo usare ovunque — e' quale protocollo usare per ogni interfaccia del tuo sistema. Comprendere la filosofia di design dietro ciascuno ti da' il giudizio per fare quella scelta correttamente.
