Storia e design di GraphQL

GraphQL è un linguaggio di interrogazione e manipolazione dei dati per API, accompagnato da un runtime che esegue le query sui dati esistenti. È nato come alternativa alle architetture basate esclusivamente su endpoint REST, con l’obiettivo di rendere più efficiente, espressivo e prevedibile lo scambio di dati tra client e server.

Origini di GraphQL in Facebook

La storia di GraphQL inizia all’interno di Facebook nel 2012. In quegli anni l’azienda si confrontava con la rapida crescita dell’uso da mobile, dove vincoli di rete e di potenza di calcolo rendevano fragili i modelli tradizionali di consumo delle API. Le applicazioni mobili avevano bisogno di ridurre al minimo il numero di richieste al server e il volume di dati trasferiti.

Le API REST esistenti esponevano diversi endpoint specializzati. Ogni endpoint restituiva una struttura di dati predefinita, spesso troppo ampia o troppo povera rispetto a ciò che l’interfaccia utente richiedeva. Il risultato era una combinazione di over-fetching (recupero di molti più dati del necessario) e under-fetching (necessità di effettuare chiamate aggiuntive per ottenere i dati mancanti). Questa situazione portava a una complessità elevata nel codice client e a performance non ottimali, soprattutto in scenari di rete instabile.

Il team di Facebook iniziò quindi a sperimentare un modello di interrogazione centrato sul prodotto: invece di pensare alle strutture dati dal punto di vista del server, ci si chiese di quali dati avessero bisogno i client per costruire le schermate dell’applicazione. Da questa prospettiva nacque l’idea di un linguaggio che permettesse al client di descrivere con precisione la forma dei dati desiderati, lasciando al server il compito di comporli da diverse sorgenti.

Pubblicazione open source e standardizzazione

Dopo alcuni anni di uso interno, Facebook rese pubblico GraphQL nel 2015. In quello stesso periodo vennero pubblicate le prime versioni di una specifica formale, con l’obiettivo di separare il concetto di GraphQL dalle implementazioni concrete e permettere la nascita di librerie in diversi linguaggi di programmazione.

La specifica definisce il linguaggio di interrogazione, il sistema di tipi, le regole di validazione e il comportamento del runtime. Questo processo ha trasformato GraphQL da tecnologia interna a standard aperto, consentendo a comunità e aziende terze di implementare server e client compatibili mantenendo la stessa semantica di base.

Nel 2018 la governance del progetto venne trasferita dalla singola azienda alla GraphQL Foundation, ospitata dalla Linux Foundation. Questo passaggio segnò un momento importante nell’evoluzione della tecnologia, perché rafforzò la sua natura di standard aperto e indipendente da un singolo fornitore, pur mantenendo un forte legame con le origini e le prime implementazioni.

Obiettivi di design

Il design di GraphQL è guidato da alcuni principi fondamentali che ne definiscono il ruolo nel panorama delle API moderne. Questi principi mirano a conciliare le esigenze dei team di prodotto, degli sviluppatori front-end e back-end e delle organizzazioni che mantengono servizi complessi.

Centralità del prodotto e del client

GraphQL nasce esplicitamente come tecnologia orientata al prodotto. Ciò significa che l’API viene progettata partendo dalle esigenze dell’esperienza utente, anziché dalla struttura interna dei database o dei microservizi. Il client descrive i dati che gli servono per costruire una determinata schermata, e il server espone uno schema che rende possibili quelle interrogazioni.

Questo approccio permette ai team di front-end di evolvere le interfacce utente con maggiore autonomia, dal momento che l’aggiunta di nuovi campi nello schema può spesso avvenire in modo compatibile senza richiedere la creazione di nuovi endpoint o versioni separate dell’API.

Schema fortemente tipizzato

Al centro del design di GraphQL c’è uno schema fortemente tipizzato: una descrizione esplicita di tutti i tipi di dati esposti dall’API (oggetti, scalari, enumerazioni, interfacce, unioni) e dei campi disponibili su ciascun tipo. Lo schema definisce anche i tipi di ingresso, le operazioni di lettura e scrittura e le regole di nullabilità dei campi.

Grazie allo schema, le query possono essere validate a priori: prima ancora di contattare il server ? è possibile verificare che una richiesta faccia riferimento a tipi e campi esistenti e che rispetti la struttura prevista. Ciò favorisce l’integrazione con strumenti di sviluppo, editor, sistemi di autocompletamento e generazione automatica di tipi statici per vari linguaggi.

Singolo endpoint logico

A differenza della tradizionale esposizione REST, che tende a mappare ogni risorsa o operazione su un endpoint distinto, GraphQL preferisce un singolo endpoint logico tramite il quale transitano tutte le interrogazioni. La differenza tra le richieste non è data tanto dall’URL quanto dal contenuto della query.

Questo modello semplifica la struttura dell’API dal punto di vista del client, perché elimina la necessità di conoscere una moltitudine di percorsi e parametri. Allo stesso tempo, sposta parte della complessità sul server, che deve interpretare la query, decomporla per le varie sorgenti di dati e ricomporre la risposta nella forma richiesta.

Struttura a grafo dei dati

Il nome GraphQL richiama la natura a grafo delle relazioni tra entità. Lo schema descrive come i tipi si collegano tra loro, e le query sfruttano questi collegamenti per navigare in profondità attraverso le relazioni: ad esempio, partendo da un utente per arrivare ai suoi amici, ai post, ai commenti e così via.

Questo modello di navigazione consente al client di esprimere, in un’unica richiesta, strutture di dati che in un’architettura REST richiederebbero spesso più chiamate consecutive. La forma della risposta riflette fedelmente la forma della query, facilitando la mappatura tra risultati e struttura dell’interfaccia utente.

Introspezione e strumenti

Un aspetto distintivo del design di GraphQL è la possibilità di interrogare lo schema stesso tramite meccanismi di introspezione. Un client può chiedere quali tipi sono disponibili, quali campi espone un certo tipo, quali argomenti accetta un determinato campo e altre informazioni strutturali.

L’introspezione è fondamentale per l’ecosistema di strumenti che ruota attorno a GraphQL: interfacce grafiche per esplorare l’API, generazione automatica di documentazione, compilatori di query, sistemi di linting e di refactoring. Queste capacità rientrano coerentemente nel principio di un linguaggio fortemente tipizzato e auto-descrivente.

Separazione tra schema e implementazione

La specifica di GraphQL si concentra sul linguaggio, sul sistema di tipi e sulle regole di esecuzione, ma non vincola il modo in cui i dati vengono effettivamente reperiti. Il server, internamente, può integrare database relazionali, archivi a documento, servizi esterni o altri sistemi.

Questa separazione consente di migrare o combinare diverse sorgenti dati senza modificare necessariamente il contratto pubblico verso i client. È uno degli aspetti che rendono GraphQL adatto a scenari di federazione di servizi e a progetti nei quali l’API funge da strato unificante di un’architettura distribuita.

Componenti fondamentali del modello

Anche senza entrare nei dettagli sintattici, è utile descrivere i principali concetti che compongono il modello di GraphQL, in modo da comprenderne la logica interna.

Tipi e schema

Il sistema di tipi definisce quali dati sono disponibili e come sono collegati. I tipi oggetto rappresentano entità come utenti, articoli o ordini; i tipi scalari rappresentano valori primitivi come numeri e stringhe; le enumerazioni rappresentano insiemi finiti di valori; le interfacce e le unioni permettono di modellare gerarchie e alternative.

Lo schema definisce inoltre il punto di ingresso alle operazioni: in genere sono presenti tipi dedicati alle interrogazioni di lettura, alle operazioni di modifica e alle sottoscrizioni per aggiornamenti in tempo reale. Ogni campo può avere argomenti che permettono di filtrare, paginare o personalizzare il risultato.

Operazioni di lettura, scrittura e sottoscrizione

Le operazioni di lettura permettono di ottenere dati in modo stratificato e gerarchico, seguendo le relazioni definite nello schema. Il client sceglie con cura quali campi richiedere, evitando di scaricare informazioni inutili.

Le operazioni di scrittura consentono di modificare lo stato del sistema. Sono progettate in modo da poter restituire a loro volta dati strutturati, spesso identici o simili ai dati che l’interfaccia utente dovrà mostrare immediatamente dopo l’aggiornamento.

Le sottoscrizioni, infine, permettono al client di ricevere flussi di aggiornamenti man mano che si verificano cambiamenti lato server. Anche in questo caso la forma dei dati è descritta dal client e dal relativo schema, rafforzando la coerenza del modello tra lettura, scrittura e notifiche.

Risoluzione dei campi

La fase di esecuzione di una query è concettualmente basata su una serie di funzioni che recuperano i valori di ogni campo richiesto. Ogni campo può essere risolto in modo indipendente, pur rispettando le dipendenze logiche definite dalla struttura della query.

Questo meccanismo rende naturale integrare GraphQL con sistemi di caching, con logica di autorizzazione a livello di campo e con ottimizzazioni mirate a ridurre i round trip verso i database o i servizi sottostanti. Allo stesso tempo, la flessibilità delle query richiede attenzione nel controllare la complessità computazionale e nell’evitare richieste troppo pesanti.

GraphQL e REST a confronto

GraphQL è spesso presentato come alternativa a REST, ma in realtà il suo design mira a risolvere problemi specifici legati alla gestione di dataset complessi e alla necessità di servire diversi client con esigenze eterogenee.

Le API REST tradizionali sono fortemente radicate nel protocollo HTTP: le operazioni sono modellate tramite metodi come GET, POST, PUT e DELETE, e la struttura delle risposte è spesso legata all’implementazione interna del server. L’evoluzione dell’API richiede frequenti modifiche agli endpoint o la creazione di nuove versioni, con il rischio di accumulare ridondanze.

GraphQL, invece, offre una visione unificata del grafo dei dati. I client possono comporre query che attraversano più entità collegate, mentre il server nasconde i dettagli della composizione interna. Questo riduce la proliferazione di endpoint specializzati e sposta l’attenzione dalla risorsa all’insieme di dati necessari all’esperienza utente.

D’altra parte, l’elevata espressività delle query introduce nuove sfide. Ad esempio, il caching lato server è meno immediato, poiché le risposte non sono rigidamente legate a un singolo URL. Inoltre, è necessario implementare meccanismi di controllo della complessità per prevenire interrogazioni eccessivamente costose che possano compromettere la stabilità del sistema.

Evoluzione della specifica e dell’ecosistema

Nel tempo la specifica di GraphQL è stata ampliata e raffinata, introducendo chiarimenti, nuove funzionalità e formalizzazioni del sistema di tipi. La definizione testuale degli schemi, spesso chiamata Schema Definition Language, è diventata un punto di riferimento per descrivere e condividere gli schemi in modo leggibile dagli sviluppatori.

Intorno alla specifica si è sviluppato un ecosistema ricco di librerie server e client in numerosi linguaggi di programmazione, nonché strumenti dedicati alla documentazione, al testing e al monitoraggio delle API. Molte aziende hanno adottato GraphQL come strato di aggregazione sopra architetture esistenti, usando il linguaggio come “collettore” dei vari servizi.

Uno dei filoni di evoluzione più rilevanti riguarda i modelli di federazione: la possibilità di combinare schemi provenienti da più servizi indipendenti in un’unica vista coerente per il client. In questo scenario, GraphQL funge da linguaggio comune per descrivere il grafo globale dei dati, mantenendo al contempo la responsabilità locale di ciascun servizio.

Impatto sul modo di progettare le API

L’introduzione di GraphQL ha avuto un impatto significativo sul modo di pensare le API. La presenza di uno schema esplicito, la centralità del client e la natura dichiarativa delle query spingono i team a modellare i dati in funzione dei casi d’uso, anziché della sola struttura dei sistemi interni.

Inoltre, la possibilità di evolvere uno schema in modo incrementale, aggiungendo campi e tipi senza rompere i client esistenti, incoraggia pratiche di versionamento più morbide rispetto al modello REST basato su nuove versioni di endpoint. La gestione della compatibilità diventa in gran parte una questione di disciplina nella deprecazione e nell’introduzione di modifiche allo schema.

Dal punto di vista culturale, GraphQL ha contribuito a rafforzare la collaborazione tra team front-end e back-end. La progettazione dello schema diventa un’attività condivisa, nella quale le esigenze di interfaccia e le considerazioni sulle prestazioni lato server devono trovare un equilibrio comune.

Conclusione

GraphQL è passato nel giro di pochi anni da soluzione interna per affrontare problemi specifici delle applicazioni Facebook a standard aperto ampiamente adottato. Il suo design, fondato su un sistema di tipi forte, su un modello a grafo dei dati e su una chiara separazione tra schema e implementazione, ha ridefinito le aspettative nei confronti delle API moderne.

Pur non essendo adatto a ogni contesto, GraphQL offre un insieme coerente di concetti e strumenti per affrontare applicazioni complesse, con molte viste diverse sugli stessi dati e con la necessità di evolvere i prodotti rapidamente. La sua storia e il suo design mostrano come una sfida concreta, legata alle limitazioni del mobile e alla complessità dei sistemi distribuiti, possa portare alla nascita di un nuovo paradigma di interrogazione e di modellazione delle API.

Torna su