Executive summary
Quando diversi sistemi software devono scambiare dati tra loro, ad esempio un'applicazione mobile che richiede informazioni a un server remoto, la progettazione dell'interfaccia di comunicazione determina in larga misura l'efficienza, la flessibilità e la manutenibilità dell'intero sistema. Esistono oggi due approcci principali per costruire queste interfacce, ciascuno nato in un contesto diverso e con filosofie opposte: il primo organizza i dati come risorse accessibili attraverso operazioni standardizzate, il secondo consente al richiedente di specificare con precisione quali informazioni desidera ricevere. Questo articolo analizza in profondità i due paradigmi, confrontandone l'architettura, le prestazioni, la sicurezza e il comportamento in produzione. L'analisi mostra che nessuno dei due approcci è universalmente superiore: la scelta più efficace dipende dalla complessità dei dati, dal numero e dalla diversità dei client, e dai requisiti operativi specifici del sistema, con una tendenza crescente verso architetture ibride che adottano entrambi i paradigmi in modo complementare.
Background
La progettazione di interfacce applicative (API) per sistemi distribuiti rappresenta una delle decisioni architetturali più consequenti nello sviluppo di software moderno. Un'API definisce il contratto attraverso cui componenti indipendenti comunicano, e le proprietà strutturali di questo contratto (granularità, accoppiamento, evolvibilità) influenzano direttamente la scalabilità e la manutenibilità dell'intero sistema [1, 2]. La rilevanza pratica di questa decisione è amplificata dal contesto contemporaneo delle architetture a microservizi, dove il numero di interfacce inter-servizio cresce in modo combinatorio con il numero di componenti, e dalla proliferazione di client eterogenei (applicazioni web, mobile, IoT) che accedono agli stessi servizi di backend con requisiti di dati profondamente diversi.
Il paradigma REST (Representational State Transfer) è stato formalizzato da Fielding nella sua dissertazione dottorale del 2000 come un insieme di vincoli architetturali derivati dall'analisi degli stili software per sistemi di rete [1]. REST non è un protocollo né una specifica implementativa, ma uno stile architetturale che prescrive sei vincoli (separazione client-server, statelessness, cacheability, interfaccia uniforme, sistema stratificato e code-on-demand opzionale) le cui interazioni producono proprietà desiderabili di scalabilità, semplicità e visibilità delle interazioni. L'adozione massiva di REST come paradigma dominante per le API web nell'arco degli anni 2000-2010 ha consolidato un modello in cui le risorse, identificate da URI, sono manipolate attraverso i metodi standard del protocollo HTTP (GET, PUT, POST, DELETE), la cui semantica è definita dagli standard IETF [3].
GraphQL è stato sviluppato internamente da Facebook a partire dal 2012 e rilasciato come specifica aperta nel 2015 [4]. A differenza di REST, GraphQL è un linguaggio di query tipizzato con un modello di esecuzione definito formalmente: il client dichiara la struttura esatta dei dati necessari, e il server restituisce una risposta la cui forma corrisponde alla query [4, 5]. La motivazione originaria era risolvere i problemi di overfetching e underfetching che l'applicazione mobile di Facebook sperimentava con le API REST esistenti, dove ciascun endpoint restituiva strutture dati fisse che raramente corrispondevano alle necessità specifiche di ciascuna vista dell'interfaccia. La specifica è attualmente mantenuta dalla GraphQL Foundation sotto la Linux Foundation, con la versione più recente pubblicata nell'ottobre 2021 [4].
Il dibattito tra REST e GraphQL non è riducibile a una semplice comparazione di prestazioni. I due paradigmi incarnano filosofie progettuali fondamentalmente diverse: REST adotta un modello resource-oriented dove l'intelligenza risiede nella semantica uniforme delle operazioni HTTP, mentre GraphQL adotta un modello query-oriented dove l'intelligenza risiede nella capacità del client di specificare con granularità arbitraria i dati necessari [5, 6]. Questa differenza ha implicazioni profonde su caching, sicurezza, evolvibilità del contratto API e complessità operativa, che costituiscono gli assi lungo i quali si sviluppa l'analisi comparativa che segue.
Criteri di confronto
L'analisi comparativa tra REST e GraphQL richiede un framework multidimensionale che superi la semplice misurazione di latenza o throughput. I criteri selezionati riflettono le preoccupazioni architetturali rilevanti per sistemi in produzione a scala enterprise, e sono organizzati lungo cinque dimensioni ortogonali.
Modello dei dati e granularità del contratto. Il primo criterio riguarda il modo in cui ciascun paradigma modella e espone i dati. Questo include la granularità della risposta (dati fissi vs. dati selezionati dal client), il trattamento delle relazioni tra entità, e il grado di accoppiamento tra struttura dell'API e struttura del client. L'analisi di Brito et al. [7] ha dimostrato empiricamente che la migrazione da REST a GraphQL riduce la dimensione delle risposte JSON del 94% in numero di campi e del 99% in byte, un risultato che quantifica l'impatto dell'overfetching nei sistemi REST.
Prestazioni e efficienza di rete. Il secondo criterio misura il comportamento in termini di latenza, throughput, utilizzo di CPU e volume di dati trasferiti. I risultati sperimentali di Niswar et al. [8] su architetture a microservizi indicano che REST presenta tempi di risposta inferiori a GraphQL per singole operazioni, mentre GraphQL consente di ridurre il numero complessivo di richieste necessarie per comporre viste complesse, un trade-off la cui risultante dipende dal pattern di accesso ai dati specifico del caso d'uso.
Caching e infrastruttura HTTP. Il terzo criterio valuta la compatibilità con i meccanismi di caching a livello di trasporto e applicazione. In un'architettura distribuita, la capacità di cacheare le risposte a diversi livelli della catena di comunicazione (browser, CDN, reverse proxy, application server) è un fattore determinante per le prestazioni percepite e per la riduzione del carico sui servizi backend. REST sfrutta nativamente la semantica di caching HTTP (ETag, Cache-Control, conditional GET), consentendo a ogni intermediario di servire risposte cached senza conoscere la logica applicativa. GraphQL opera tipicamente su un singolo endpoint POST, rendendo il caching a livello HTTP inefficace senza interventi architetturali specifici come persisted queries o normalized caching lato client [5, 9].
Sicurezza e controllo degli accessi. Il quarto criterio esamina la superficie di attacco e i meccanismi di protezione disponibili in ciascun paradigma. Il modello di sicurezza è particolarmente critico per API esposte pubblicamente o consumate da client non fidati, dove la possibilità di costruire richieste arbitrarie amplifica il rischio di abuso. REST beneficia di un modello di autorizzazione coarse-grained basato su endpoint e metodi HTTP, compatibile con middleware standard e API gateway. GraphQL introduce vettori di attacco specifici (query ricorsive, introspezione non controllata, batching di operazioni) che richiedono contromisure dedicate e una superficie di sicurezza più complessa da governare [10, 11].
Evolvibilità e governance del contratto. Il quinto criterio analizza le strategie di versionamento, la gestione delle breaking change e il supporto per l'evoluzione incrementale dell'API nel tempo. La capacità di modificare il contratto API senza interrompere i client esistenti è un requisito operativo fondamentale per sistemi con cicli di vita pluriennali e consumer multipli. REST affronta il versionamento tipicamente attraverso URI versioned o content negotiation, richiedendo ai client una migrazione esplicita. GraphQL adotta un modello di deprecation granulare a livello di campo, dove il contratto è additivo e i client esistenti non sono impattati dall'aggiunta di nuove funzionalità [4, 12].
Analisi comparativa
REST: architettura resource-oriented
Fondamenti e vincoli architetturali
L'architettura REST si fonda sul principio che ogni concetto rilevante del dominio è modellato come una risorsa, identificata univocamente da un URI, e manipolata attraverso un'interfaccia uniforme costituita dai metodi HTTP [1]. Questa uniformità è il meccanismo principale attraverso cui REST raggiunge la scalabilità: poiché ogni intermediario nella catena di comunicazione (proxy, gateway, CDN) comprende la semantica dei metodi HTTP senza dover ispezionare il payload, le proprietà di cacheability, safety e idempotency sono preservate end-to-end [1, 3].
Richardson ha proposto un modello di maturità a quattro livelli che classifica le API in funzione del grado di aderenza ai vincoli REST [13]. Al livello 0, un singolo endpoint con un singolo metodo HTTP gestisce tutte le operazioni (essenzialmente, RPC su HTTP). Al livello 1, vengono introdotte risorse individuali con URI distinti. Al livello 2, i metodi HTTP sono utilizzati con la loro semantica corretta (GET per lettura, POST per creazione, PUT per aggiornamento idempotente, DELETE per rimozione). Al livello 3, le risposte includono controlli hypermedia (HATEOAS) che consentono al client di navigare l'API in modo dinamico. In pratica, la maggior parte delle API REST in produzione si colloca al livello 2, adottando la semantica dei metodi HTTP ma omettendo i controlli hypermedia [13, 6].
Il vincolo di statelessness, secondo cui ogni richiesta deve contenere tutte le informazioni necessarie per essere elaborata senza fare affidamento su contesto memorizzato sul server, ha implicazioni dirette sulla scalabilità orizzontale. Un server stateless può essere replicato liberamente dietro un load balancer senza necessità di session affinity, e qualsiasi istanza può servire qualsiasi richiesta. Questa proprietà, combinata con la cacheability nativa, ha reso REST il paradigma dominante per le API web pubbliche nell'era delle architetture cloud [1, 6].
Limiti strutturali
Il modello resource-oriented introduce tuttavia problemi strutturali quando i requisiti del client divergono dalla granularità delle risorse esposte. Il fenomeno dell'overfetching si manifesta quando un endpoint restituisce un insieme di campi significativamente più ampio di quanto necessario per una specifica vista del client. Simmetricamente, l'underfetching si verifica quando una singola vista richiede dati provenienti da risorse multiple, costringendo il client a eseguire richieste sequenziali o parallele per comporre il risultato necessario [5, 7].
Questi problemi sono esacerbati dalla proliferazione di client eterogenei. Si consideri un esempio concreto: un'applicazione mobile che visualizza una lista compatta di utenti necessita esclusivamente di nome e avatar, mentre un'applicazione desktop che mostra un profilo dettagliato necessita di biografia, contatti, ruolo, data di registrazione e relazioni con altri utenti. L'endpoint REST GET /users/123 restituisce invariabilmente l'intero oggetto utente, indipendentemente da quale client ha effettuato la richiesta:
// REST: GET /users/123 - risposta fissa (overfetching per client mobile)
{
"id": 123,
"name": "Alice Rossi",
"avatar": "/img/alice.png",
"email": "alice@example.com",
"bio": "Senior engineer...",
"role": "admin",
"created_at": "2023-01-15",
"department": { "id": 7, "name": "Engineering" },
"projects": [ ... ]
}
Con GraphQL, ciascun client dichiara esattamente i campi necessari:
# GraphQL: query mobile - solo i campi necessari
{
user(id: 123) {
name
avatar
}
}
In un'architettura REST, la risposta al dilemma della granularità tipicamente assume una di tre forme: endpoint unici con payload massimale (overfetching per i client leggeri), endpoint specializzati per ciascun tipo di client (esplosione combinatoria degli endpoint e duplicazione della logica), o meccanismi di sparse fieldsets e field selection (che introducono complessità nel design dell'endpoint senza la formalizzazione offerta da un linguaggio di query) [5, 7, 12].
Uno studio empirico di Vogel et al. [14] ha evidenziato un ulteriore limite strutturale: mentre REST promuove il disaccoppiamento tra client e server a livello di interfaccia, in pratica i client sviluppano un accoppiamento forte con le policy di evoluzione del provider. Quando un provider rilascia una nuova versione dell'API e dismette la precedente, i client sono obbligati ad adattarsi, creando un costo di manutenzione distribuito che cresce proporzionalmente al numero di consumer.
GraphQL: architettura query-oriented
Fondamenti e type system
GraphQL inverte il rapporto di controllo tra client e server rispetto a REST. Invece di un insieme di endpoint che espongono strutture dati predeterminate, un singolo endpoint accetta query espresse in un linguaggio dichiarativo tipizzato, e il server restituisce esclusivamente i campi esplicitamente richiesti [4]. La specifica definisce tre tipi di operazione: query (lettura), mutation (scrittura) e subscription (streaming di eventi in tempo reale via WebSocket o SSE).
Il cuore del paradigma è il type system: ogni schema GraphQL definisce un grafo di tipi (scalari, oggetti, enum, interfacce, union) e le relazioni tra essi, che il server implementa attraverso funzioni di risoluzione (resolver) associate a ciascun campo [4]. L'introspezione, ovvero la capacità di interrogare lo schema stesso, è una proprietà strutturale del linguaggio: il client può scoprire a runtime i tipi, i campi e le relazioni disponibili, abilitando tooling avanzato come autocompletamento, validazione statica delle query e generazione automatica di codice [4, 5].
Wittern et al. [15] hanno condotto il primo studio empirico su larga scala degli schemi GraphQL, analizzando 16 schemi commerciali e 8.399 schemi estratti da repository GitHub. Lo studio ha identificato pattern ricorrenti nella progettazione degli schemi e ha rilevato che la maggior parte delle API esaminate risulta vulnerabile a denial of service attraverso query complesse, evidenziando un problema di sicurezza intrinseco al modello [15].
Risoluzione delle query e modello di esecuzione
Il modello di esecuzione di GraphQL è strutturalmente diverso da quello REST. Una query GraphQL attraversa lo schema come un grafo, invocando il resolver associato a ciascun campo richiesto. Questo meccanismo consente al client di ottenere in una singola richiesta dati che in un'architettura REST richiederebbero chiamate multiple a endpoint diversi, eliminando il problema dell'underfetching. Tuttavia, introduce un pattern di esecuzione potenzialmente problematico: il cosiddetto N+1 problem, dove la risoluzione di un campo relazionale per N entità genera N query aggiuntive al data source sottostante [5, 16].
La soluzione standard a questo problema è il pattern DataLoader, originariamente sviluppato internamente da Facebook e successivamente rilasciato come libreria open-source [16]. DataLoader implementa un meccanismo di batching e caching a livello di richiesta: le chiamate individuali al data source vengono accumulate durante un singolo tick del ciclo di esecuzione e risolte in una singola operazione batch. L'implementazione originale, descritta dagli ingegneri di Facebook come evoluzione del framework "Ent" utilizzato internamente dal 2010, è divenuta un pattern architetturale fondamentale per qualsiasi implementazione GraphQL in produzione [16].
Limiti strutturali
Il principale limite architetturale di GraphQL è la perdita della semantica HTTP nativa. Poiché la maggior parte delle implementazioni utilizza un singolo endpoint POST, i meccanismi di caching a livello di trasporto HTTP (ETag, Cache-Control, conditional request) risultano inefficaci [5, 9]. Mentre una richiesta REST GET a /users/123 è nativamente cacheable da proxy, CDN e browser, una query GraphQL POST con body { user(id: 123) { name email } } non lo è, richiedendo l'implementazione di strategie di caching a livello applicativo (normalized cache lato client, persisted queries, o cache key derivate dal contenuto della query) [9].
Il secondo limite è la complessità operativa nella gestione della sicurezza. In un'architettura REST, l'autorizzazione può essere implementata a livello di endpoint e metodo HTTP, con granularità relativamente coarse-grained: un middleware può autorizzare l'accesso a GET /users/{id} indipendentemente dalla struttura della risposta. In GraphQL, l'autorizzazione deve operare a livello di campo e di resolver, poiché il client può richiedere combinazioni arbitrarie di dati in una singola query [10, 11]. Inoltre, la flessibilità delle query introduce vettori di attacco specifici: query ricorsive profondamente annidate possono esaurire le risorse del server (denial of service), l'introspezione non controllata espone la struttura completa dello schema, e il batching di query tramite alias consente di aggirare i rate limiter basati su conteggio di richieste HTTP [10, 11].
Le linee guida OWASP per la sicurezza di GraphQL raccomandano contromisure specifiche: limitazione della profondità delle query, analisi della complessità computazionale con query cost analysis, disabilitazione dell'introspezione in produzione, rate limiting per operazione anziché per richiesta HTTP, e whitelisting delle query per applicazioni con pattern di accesso prevedibili [11].
Discussione e raccomandazioni
Convergenza e pattern ibridi
L'analisi comparativa evidenzia che REST e GraphQL non si collocano su un asse unidimensionale di "migliore" e "peggiore", ma occupano regioni diverse di uno spazio di trade-off multidimensionale. La pratica industriale ha consolidato questa consapevolezza attraverso l'adozione crescente di architetture ibride in cui i due paradigmi coesistono con ruoli complementari.
Il pattern architetturale più maturo in questa direzione è il Backend for Frontend (BFF), formalizzato da Newman [17] come variante specializzata dell'API gateway. In un'architettura BFF con GraphQL, i servizi backend espongono API REST o gRPC ottimizzate per le proprie responsabilità di dominio, mentre uno strato intermedio GraphQL aggrega, trasforma e modella i dati in funzione delle necessità specifiche di ciascun tipo di client [17]. Questo approccio consente di preservare i vantaggi operativi di REST a livello di microservizi (cacheability, semplicità, aderenza alla semantica HTTP), delegando a GraphQL la composizione flessibile dei dati per i client frontend.
Apollo Federation rappresenta l'implementazione più adottata di questo pattern a scala enterprise [9]. L'architettura prevede un router centrale che riceve le query GraphQL dai client e le decompone in sub-query dirette ai subgraph pertinenti, ciascuno dei quali implementa una porzione dello schema complessivo. La composizione dello schema avviene a build-time attraverso un processo dichiarativo, e il supergraph risultante è il contratto API esposto ai client [9]. Questo modello consente a team indipendenti di evolvere i propri subgraph in modo autonomo, preservando la coerenza dello schema complessivo attraverso un meccanismo di composizione verificabile.
Il diagramma seguente illustra l'architettura ibrida in cui GraphQL opera come strato BFF sopra servizi backend REST e gRPC:
graph TD
A[Client Web] -->|Query GraphQL| GW[GraphQL Gateway / BFF]
B[Client Mobile] -->|Query GraphQL| GW
C[Client IoT] -->|REST diretto| S1
GW -->|REST| S1[Servizio Utenti]
GW -->|REST| S2[Servizio Ordini]
GW -->|gRPC| S3[Servizio Inventario]
S1 --> DB1[(Database Utenti)]
S2 --> DB2[(Database Ordini)]
S3 --> DB3[(Database Inventario)]
style GW fill:#e1f0ff,stroke:#4a90d9,stroke-width:2px
style A fill:#f9f9f9,stroke:#999
style B fill:#f9f9f9,stroke:#999
style C fill:#f9f9f9,stroke:#999
Figura 1. Architettura ibrida BFF: il gateway GraphQL aggrega dati da microservizi REST e gRPC, mentre i client con requisiti semplici (es. IoT) possono accedere direttamente ai servizi REST.
Evolvibilità del contratto API
La gestione dell'evoluzione dell'API nel tempo costituisce una differenza architetturale significativa tra i due paradigmi. In REST, il versionamento del contratto segue tipicamente una di due strategie: versionamento nell'URI (/v1/users, /v2/users) o content negotiation tramite header Accept con media type versioned [3, 12]. Entrambe le strategie richiedono al provider di mantenere versioni multiple dell'API in parallelo durante il periodo di transizione, e ai consumer di migrare esplicitamente alla nuova versione. La specifica OpenAPI [12] fornisce un framework per documentare le differenze tra versioni, ma non risolve il costo operativo della manutenzione parallela.
GraphQL adotta un approccio strutturalmente diverso: lo schema è un contratto unico, additivo e versionato implicitamente. Nuovi campi e tipi possono essere aggiunti senza impatto sui client esistenti, poiché ciascun client richiede esplicitamente solo i campi che conosce. La rimozione di campi avviene attraverso un meccanismo di deprecation a livello di schema: il campo viene annotato con @deprecated(reason: "...") e rimane disponibile ma segnalato come obsoleto nel tooling di introspezione [4]. Questo modello elimina la necessità di endpoint versioned e riduce il costo di coordinamento tra provider e consumer, ma introduce il rischio di accumulazione di campi deprecati che non vengono mai rimossi (schema bloat), un problema che richiede governance attiva e metriche di utilizzo per campo [6, 15].
Matrice decisionale
La scelta tra REST e GraphQL è determinata dall'intersezione di fattori specifici del contesto di progetto. Si propone una matrice decisionale basata sui criteri emersi dall'analisi.
REST risulta preferibile quando: l'API è pubblica e i consumer sono eterogenei e non controllati dal provider; i pattern di accesso sono relativamente stabili e prevedibili; il caching a livello HTTP è un requisito critico per le prestazioni; l'infrastruttura esistente (CDN, API gateway, monitoring) è ottimizzata per il modello request-response HTTP; la semplicità operativa e la curva di apprendimento ridotta sono prioritarie.
GraphQL risulta preferibile quando: i client hanno requisiti di dati profondamente eterogenei (mobile vs. desktop vs. IoT) e una stessa query può richiedere dati provenienti da entità multiple; il grafo dei dati è complesso e le relazioni tra entità sono frequentemente attraversate nelle query del client; l'API è consumata da team interni o da un numero controllato di client, dove la complessità aggiuntiva della sicurezza è gestibile; la riduzione dell'overfetching ha un impatto misurabile sulle prestazioni, in particolare per client con banda limitata.
Un approccio ibrido è raccomandato quando: l'architettura comprende microservizi interni con comunicazione point-to-point (dove REST o gRPC sono appropriati) e client frontend diversificati che richiedono aggregazione flessibile dei dati; l'organizzazione dispone di team frontend e backend con ownership distinta, dove GraphQL agisce come contratto di interfaccia negoziato; il sistema è in evoluzione e la migrazione completa da un paradigma all'altro comporterebbe rischi e costi non giustificati.
Evoluzione e tendenze
La systematic mapping study di Quiña-Mera et al. [6], che ha analizzato 84 studi primari selezionati da un corpus iniziale di 3.185, ha rilevato che l'adozione di GraphQL è in crescita costante, ma che la base di evidenza empirica rimane insufficiente, in particolare per studi condotti in contesti industriali reali. Questa osservazione è coerente con il dato emerso dalla ricerca: mentre i risultati sperimentali in ambiente controllato sono numerosi [7, 8], le valutazioni sistematiche in produzione a scala enterprise sono rare e spesso limitate a case study di singole organizzazioni.
Un caso emblematico è la migrazione di GitHub da REST a GraphQL, annunciata nel 2016 [18]. La motivazione dichiarata era la difficoltà di scalare il modello REST per servire client con requisiti di dati profondamente diversi: l'API REST v3 richiedeva due o tre chiamate separate per comporre una vista completa di una risorsa, e le risposte contenevano simultaneamente troppi dati per alcuni consumer e dati insufficienti per altri [18]. L'adozione di GraphQL ha consentito a GitHub di esporre un'API self-service in cui ciascun client dichiara esattamente i dati necessari, riducendo significativamente sia il volume di dati trasferiti sia il numero di richieste. GitHub mantiene tuttavia l'API REST v3 in parallelo, confermando il pattern di coesistenza piuttosto che sostituzione.
Le specifiche OpenAPI [12] e la specifica GraphQL [4] continuano a evolversi in modo indipendente, con una convergenza implicita su alcuni temi: la necessità di contratti formali verificabili, l'importanza della generazione automatica di tooling dalla definizione dell'API, e il riconoscimento che la documentazione eseguibile (schema introspectable in GraphQL, specifica machine-readable in OpenAPI) è un prerequisito per la governance a scala. La tendenza emergente non è la vittoria di un paradigma sull'altro, ma la maturazione di entrambi come strumenti complementari all'interno di un portfolio architetturale che include anche gRPC per la comunicazione inter-servizio ad alte prestazioni e protocolli event-driven per i flussi asincroni [8].
Riferimenti
[1] R. T. Fielding, "Architectural Styles and the Design of Network-based Software Architectures," Doctoral dissertation, University of California, Irvine, 2000. https://ics.uci.edu/~fielding/pubs/dissertation/top.htm
[2] M. Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017.
[3] R. Fielding, J. Reschke (Eds.), "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content," RFC 7231, IETF, 2014. https://datatracker.ietf.org/doc/html/rfc7231
[4] GraphQL Foundation, "GraphQL Specification," October 2021. https://spec.graphql.org/October2021/
[5] GraphQL Foundation, "GraphQL: The Query Language for Modern APIs," 2024. https://graphql.org/
[6] A. Quiña-Mera, P. Fernandez, J. M. García, A. Ruiz-Cortés, "GraphQL: A Systematic Mapping Study," ACM Computing Surveys, Vol. 55, No. 10, Article 202, 2023. https://dl.acm.org/doi/10.1145/3561818
[7] G. Brito, T. Mombach, M. T. Valente, "Migrating to GraphQL: A Practical Assessment," in Proc. 26th IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), 2019. https://arxiv.org/abs/1906.07535
[8] M. Niswar, R. A. Safruddin, A. Bustamin, I. Aswad, "Performance Evaluation of Microservices Communication with REST, GraphQL, and gRPC," International Journal of Electronics and Telecommunications, Vol. 70, No. 2, pp. 429-436, 2024. https://journals.pan.pl/Content/131803
[9] Apollo GraphQL, "Apollo Federation Documentation," 2024. https://www.apollographql.com/docs/federation/
[10] GraphQL Foundation, "GraphQL Security," 2024. https://graphql.org/learn/security/
[11] OWASP Foundation, "GraphQL Cheat Sheet," OWASP Cheat Sheet Series, 2024. https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html
[12] OpenAPI Initiative, "OpenAPI Specification v3.1.0," 2024. https://spec.openapis.org/oas/v3.1.0
[13] M. Fowler, "Richardson Maturity Model," 2010. https://martinfowler.com/articles/richardsonMaturityModel.html
[14] M. Vogel et al., "Web API Growing Pains: Loosely Coupled yet Strongly Tied," Journal of Systems and Software, Vol. 100, 2015. https://www.sciencedirect.com/science/article/abs/pii/S0164121214002180
[15] E. Wittern, A. Cha, J. C. Davis, G. Baudart, L. Mandel, "An Empirical Study of GraphQL Schemas," in Proc. 17th International Conference on Service-Oriented Computing (ICSOC), Springer LNCS Vol. 11895, 2019. https://arxiv.org/abs/1907.13012
[16] GraphQL Foundation, "DataLoader — Batching and Caching for GraphQL," GitHub repository, 2024. https://github.com/graphql/dataloader
[17] S. Newman, "Backends For Frontends," 2015. https://samnewman.io/patterns/architectural/bff/
[18] GitHub Engineering, "GitHub Adopts New GraphQL API," InfoQ, October 2016. https://www.infoq.com/news/2016/10/graphQL-GitHub/