vai al contenuto principale

Introduzione alla API GraphQL

GraphQL è un potente strumento per interrogare applicazioni Livebase e manipolare dati mediante un linguaggio di query preciso, flessibile, prevedibile e interoperabile.

Livebase adotta e promuove GraphQL come mezzo principale di accesso alle applicazioni generate, perché offre una flessibilità notevolmente maggiore ai nostri integratori rispetto alla API REST tradizionale. L’approccio a grafo e il sistema di tipi di GraphQL sono in linea con molti principi della modellazione Livebase.

Livebase genera una API GraphQL distinta per ogni vista applicativa (Application Schema) definita nel modello di una Cloudlet abilitata. La Cloudlet espone un endpoint che offre una API basata sullo Schema GraphQL generato a partire dalle classi, attributi e relazioni gestite su quella vista applicativa; è possibile interrogare l’endpoint per leggere e/o modificare grafi di oggetti o persino invocare plugin.

In questa guida:

Collegamenti rapidi #

Ecco alcuni link utili per iniziare a utilizzare la API GraphQL di Livebase:

Cos’è GraphQL #

GraphQL è un meccanismo di accesso per API che fornisce un’alternativa più efficiente, potente e flessibile ai classici schemi, e in particolare al paradigma REST. Nato per far fronte all’aumento del traffico internet proveniente da applicazioni mobile e alla diffusione di framework sempre più complessi ed eterogenei, GraphQL è stato sviluppato da Facebook nel 2012, inizialmente come progetto interno, ed è stato poi reso open source a partire dal 2015. Oggi, GraphQL è adottato in larga scala e gestito dalla GraphQL Foundation, composta da compagnie leader del settore come Airbnb, Facebook, GitHub e Twitter.

GraphQL propone un approccio dichiarativo nel recupero dei dati, in cui i client specificano esattamente quali informazioni vogliono recuperare dalla API, riponendo particolare attenzione sulla prevedibilità: i dati e servizi che compongono la API GraphQL sono descritti in termini di tipi di dati, dove ciascun tipo è costituito da uno o più campi contenente una propria specifica di tipo. I client hanno quindi a disposizione uno schema GraphQL che documenta con chiarezza tutti i servizi della API: cosa fanno, cosa ricevono in input e cosa ritornano.

Lo schema GraphQL risiede sul server ed è usato dal runtime system come punto d’orientamento per validare le richieste e respingere le query errate; anche in caso di errori, il server è in grado di rispondere con messaggi descrittivi e prevedibili.

Linguaggio GraphQL #

Un servizio GraphQL viene creato definendo tipi e campi, e successivamente fornendo funzioni per popolare ogni campo sul tipo. Dal punto di vista pratico, i servizi possono essere scritti in qualunque linguaggio; per questo motivo, GraphQL adotta una lingua franca per descrivere lo schema ai client, chiamata Schema Definition Language (SDL). Questo linguaggio astratto e indipendente dall’implementazione serve a mettere d’accordo client e server sui dati scambiati attraverso la API.

Ad esempio, il seguente schema descrive un servizio GraphQL che consente di leggere il nome e l’età di un impiegato a partire dal suo ID, e di risalire al nome del team di cui esso fa parte:

# uno schema GraphQL
type Employee {
  _id: ID
  full_name: String
  age: Int
  team: Team
}
type Team {
  _id: ID
  name: String
}
type Query {
  Employee__get(_id: ID!): Employee
}

Il linguaggio di query consiste fondamentalmente nel selezionare campi sui tipi definiti sullo schema. La risposta del server è un oggetto JSON (JavaScript Object Notation), i cui campi coincidono esattamente con quelli richiesti nella query.

# una query GraphQL
{
  Employee___get(_id: "12345") {
    full_name
    age
    team {
      name
    }
  }
}
// risposta del server
{
  "data": {
    "Employee___get": {
      "full_name": "John Smith",
      "age": 35,
      "team": {
        "name": "Cool Coders"
      }
    }
  }
}

Come vediamo nell’esempio, in GraphQL il contenuto della risposta corrisponde con i campi selezionati dalla query, ciò significa che è facile per un client prevedere il risultato di un servizio ancor prima di eseguire una query.

Differenze con REST #

GraphQL non rappresenta una rottura totale con REST, bensì prosegue sulla stessa via ereditandone i principali elementi fondanti:

  • anche GraphQL è basato sul protocollo HTTP (le chiamate GraphQL sono richieste HTTP di tipo POST), e comporta quindi un basso overhead nella comunicazione tra client e server;
  • come REST, GraphQL è un’interfaccia sottile: lo schema è riferito a strutture dati definite nell’applicazione (e non nel database), e i dati richiesti vengono restituiti indipendentemente dal database in cui sono archiviati. Aspetti come autenticazione, caching e ottimizzazione delle query non vengono gestiti direttamente da GraphQL e sono delegati allo strato applicativo sottostante.

Query flessibili #

La differenza principale con le API REST tradizionali è che, invece di mappare le risorse su endpoint multipli (ciascuno dei quali restituisce una struttura dati fissa), un server GraphQL espone un unico endpoint e permette ai client di accedere a più risorse attraverso una singola richiesta. La rigidità di REST nel legare risorse a endpoint (e di conseguenza URL) causa infatti da sempre due problemi:

  • over-fetching, ovvero la situazione in cui un endpoint risponde troppo genericamente offrendo una gran mole di dati che il client è costretto a elaborare sempre e comunque, anche se è interessato solo a un sottoinsieme di informazioni;
  • under-fetching (anche detto n+1 requests problem), situazione opposta alla precedente in cui un endpoint specifico non fornisce abbastanza informazioni al client, per cui quest’ultimo è costretto a effettuare ulteriori richieste su altri endpoint per recuperare tutto ciò di cui ha bisogno, con conseguenze negative in termini di efficienza e consumo di banda a ogni richiesta aggiuntiva (tanto per sé stesso quanto per il server).

In GraphQL non esistono richieste rigide: qualunque query che rispetti lo schema è una query valida. Questo consente ai client di scrivere query altamente specializzate nel caso d’uso d’interesse e minimizzare sia il numero di richieste inviate al server che la mole di dati scambiati. Client diversi possono quindi interagire con la stessa API in modo radicalmente diverso, consumando solo i dati di cui hanno bisogno.

Scoperta di informazioni #

Nel tempo si è cercato di rendere più flessibili le API REST tradizionali, offrendo ai client alcune libertà nello stabilire il formato di richiesta e risposta (ad esempio specificando parametri nell’URL della richiesta), oppure esponendo molti endpoint specializzati. Al crescere della complessità, tuttavia, diventa difficile documentare in maniera uniforme le informazioni offerte, in modo che possano essere consumate da un client. Documentare una API REST è una responsabilità lasciata a chi la progetta e non segue un processo standard.

In GraphQL, d’altra parte, lo schema stesso documenta in modo standard la API. L’esistenza di un unico “grafo” centralizzato (rispetto agli endpoint multipli REST) consente di scrivere query come fossero “cammini” che partono da un punto d’ingresso e raggiungono diversi “nodi” dello schema. Mentre su REST lo sviluppatore è costretto a consultare la documentazione su una pagina web, in GraphQL può richiedere direttamente allo schema informazioni relative a tipi e campi usando query introspettive, oppure usare uno strumento di sviluppo che offre funzioni di autocompletamento, come GraphiQL.

“Think in graphs, not endpoints.” 🇺🇸 Lessons From 4 Years of GraphQL.

Evoluzione e versioning #

In REST l’evoluzione delle API è sempre stato un limite: aggiornamenti al frontend richiedono spesso modifiche al backend per accomodare nuove esigenze in termini di dati richiesti, viceversa modifiche ai servizi della API possono compromettere il funzionamento delle chiamate pre-esistenti sui client. Per questi due motivi, in REST spesso si ricorre al versioning della API (raggruppando gli endpoint di una determinata versione sotto una radice comune, ad esempio /api/v3/), così da garantire retrocompatibilità e aggiornamenti graduali, ma introducendo allo stesso tempo altra complessità nel documentare i servizi esistenti.

In GraphQL, d’altra parte, non esiste il concetto di versioning: dato che le query ritornano solo i dati esplicitamente richiesti, è possibile predisporre nuovi tipi e campi senza introdurre modifiche sostanziali o compromettere la correttezza delle chiamate già in produzione. Grazie alla flessibilità del linguaggio, i client hanno molte opzioni per adattare le loro chiamate, senza richiedere lavoro aggiuntivo sul server o sullo schema.

Ulteriori letture #

Benefici #

Ricapitolando, i punti di forza di GraphQL sono:

  • forte tipizzazione: lo schema definisce il sistema dei tipi della API e tutte le relazioni tra oggetti (nonché la validità delle chiamate dei client), mentre la specifica determina la validità dello schema sul server;
  • flessibilità: i client possono navigare la gerarchia definita nello schema con query altamente specializzate che minimizzano il numero di chiamate e richiedono con precisione solamente i dati di cui hanno bisogno;
  • prevedibilità: il formato di una query GraphQL rispecchia il contenuto della risposta JSON ritornata dal server;
  • rapidità di evoluzione: lo schema può evolvere rapidamente senza introdurre versioning, mentre i client possono adattare le loro chiamate senza richiedere lavoro aggiuntivo sul server o sullo schema;
  • introspezione: i client possono interrogare lo schema per ottenere dettagli sullo schema stesso;
  • interoperabilità: GraphQL offre un ampia scelta di librerie per implementare sia il server che il client, oltre a potenti strumenti di sviluppo.