GraphQL è una nuova ed entusiasmante API per query e manipolazioni ad hoc. È estremamente flessibile e offre numerosi vantaggi. È particolarmente adatto per esporre i dati organizzati come grafici e alberi. Facebook ha sviluppato GraphQL nel 2012 e lo ha aperto nel 2015.
Decollò rapidamente e divenne una delle tecnologie più calde. Molte aziende innovative hanno adottato e utilizzato GraphQL in produzione. In questo tutorial imparerai:
GraphQL è ottimale quando i dati sono organizzati in una gerarchia o in un grafico e il front-end vorrebbe accedere a sottoinsiemi diversi di questa gerarchia o di un grafico. Considera un'applicazione che espone l'NBA. Hai squadre, giocatori, allenatori, campionati e molte informazioni su ciascuno di essi. Ecco alcune query di esempio:
Potrei inventare centinaia di domande del genere. Immagina di dover progettare un'API per esporre tutte queste query al front-end ed essere in grado di estendere facilmente l'API con nuovi tipi di query mentre gli utenti o il product manager escogitano nuove interessanti cose da interrogare.
Questo non è banale. GraphQL è stato progettato per risolvere questo problema esatto e, con un unico endpoint API, fornisce un'enorme potenza, come vedremo presto.
Prima di immergerci nei dadi e bulloni di GraphQL, confrontiamolo con REST, che è attualmente il tipo più popolare di API Web.
REST segue un modello orientato alle risorse. Se le nostre risorse sono giocatori, allenatori e squadre, probabilmente ci saranno endpoint come:
Spesso gli endpoint senza id restituiscono solo un elenco di ID e gli endpoint con l'ID restituiscono le informazioni complete su una risorsa. Puoi, ovviamente, progettare la tua API in altri modi (ad esempio l'endpoint / giocatori può restituire anche il nome di ciascun giocatore o tutte le informazioni su ciascun giocatore).
Il problema con questo approccio in un ambiente dinamico è che ti stai sottovalutando (ad esempio, ottieni solo gli ID e hai bisogno di più informazioni) o stai esagerando (ad esempio ottenendo le informazioni complete su ciascun giocatore quando sei solo interessato al nome).
Questi sono problemi difficili. Quando vengono scaricati, se si prelevano 100 ID, è necessario eseguire 100 chiamate API separate per ottenere le informazioni su ciascun lettore. Quando si esegue il trascinamento, si spreca molto tempo di back-end e larghezza di banda della rete per preparare e trasferire molti dati che non sono necessari.
Ci sono modi per affrontarlo con REST. È possibile progettare molti endpoint personalizzati, ognuno dei quali restituisce esattamente i dati necessari. Questa soluzione non è scalabile. È difficile mantenere l'API coerente. È difficile evolverlo. È difficile documentarlo e usarlo. È difficile mantenerlo quando c'è una grande sovrapposizione tra questi endpoint personalizzati.
Prendi in considerazione questi endpoint aggiuntivi:
Un altro approccio consiste nel mantenere un numero limitato di endpoint generici, ma fornire molti parametri di query. Questa soluzione evita il problema di molti endpoint, ma va contro la grana del modello REST, ed inoltre è difficile da evolvere e mantenere costantemente.
Si potrebbe dire che GraphQL ha portato questo approccio al limite. Non pensa in termini di risorse ben definite, ma invece in termini di sotto-grafici dell'intero dominio.
GraphQL modella il dominio utilizzando un sistema di tipi costituito da tipi e attributi. Ogni attributo ha un tipo. Il tipo di attributo può essere uno dei tipi di base forniti da GraphQL come ID, String e Boolean o un tipo definito dall'utente. I nodi del grafico sono i tipi definiti dall'utente ei bordi sono gli attributi che hanno tipi definiti dall'utente.
Ad esempio, se un tipo "Giocatore" ha un attributo "squadra" con il tipo "Squadra", significa che c'è uno spigolo tra ciascun nodo giocatore a un nodo squadra. Tutti i tipi sono definiti in uno schema che descrive il modello di oggetti del dominio GraphQL.
Ecco uno schema molto semplificato per il dominio NBA. Il giocatore ha un nome, una squadra a cui è maggiormente associato (sì, so che i giocatori a volte si spostano da una squadra all'altra) e il numero di campionati che il giocatore ha vinto.
La squadra ha un nome, una schiera di giocatori e il numero di campionati vinti dalla squadra.
type Player id: ID name: String! squadra: squadra! championshipCount: Integer! tipo Team id: ID name: String! giocatori: [Giocatore!]! championshipCount: Integer!
Ci sono anche punti di ingresso predefiniti. Quelli sono Query, Mutation e Subscription. Il front-end comunica con il back-end attraverso i punti di ingresso e li personalizza per le sue esigenze.
Ecco una query che restituisce semplicemente tutti i giocatori:
digita Query allPlayers: [Player!]!
Il punto esclamativo indica che il valore non può essere nullo. Nel caso del allPlayers
query, può restituire una lista vuota, ma non null. Inoltre, significa che non ci può essere un giocatore Null nell'elenco (perché contiene Player!).
Ecco un server GraphQL a tutti gli effetti basato su node-express. Ha un archivio dati con hard-coded in memoria. Normalmente, i dati saranno in un database o recuperati da un altro servizio. I dati sono definiti qui (chiedo scusa in anticipo se la tua squadra o il tuo giocatore preferito non ce l'ha fatta):
let data = "allPlayers": "1": "id": "1", "nome": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2": "id": "2", "nome": "Michael Jordan", "championshipCount": 6, "teamId": "1", "3": "id": "3", "nome": "Scottie Pippen", "championshipCount": 6, "teamId": "1", "4": "id": "4", "nome": "Magic Johnson", "championshipCount": 5, "teamId ":" 2 "," 5 ": " id ":" 5 "," nome ":" Kobe Bryant "," championshipCount ": 5," teamId ":" 2 "," 6 ": " id ":" 6 "," nome ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 ", "name": "Chicago Bulls", "championshipCount": 6, "players": [], "2": "id": "2", "name": "Los Angeles Lakers", "championshipCount": 16, "giocatori": [], "3": "id": "3", "nome": "Golden State Warriors", "championshipCount": 5, "giocatori": []
Le librerie che uso sono:
const express = require ('express'); const graphqlHTTP = require ('express-graphql'); const app = express (); const buildSchema = require ('graphql'); const _ = require ('lodash / core');
Questo è il codice per costruire lo schema. Si noti che ho aggiunto un paio di variabili al allPlayers
query di root.
schema = buildSchema ('tipo Player id: ID nome: String! championshipCount: squadra Int!: Team! tipo Team id: nome ID: String! championshipCount: giocatori Int!: [giocatore!]! tipo Query allPlayers (offset: Int = 0, limite: Int = -1): [Player!]! '
Ecco la parte fondamentale: collegare le query e servire effettivamente i dati. Il rootValue
l'oggetto può contenere radici multiple.
Qui, c'è solo il allPlayers
. Estrae l'offset e il limite dagli argomenti, suddivide i dati di tutti i giocatori e quindi imposta la squadra su ciascun giocatore in base all'id del team. Questo rende ogni giocatore un oggetto annidato.
rootValue = allPlayers: (args) => offset = args ['offset'] limit = args ['limite'] r = _.values (dati ["allPlayers"]). slice (offset) if (limite> - 1) r = r.slice (0, Math.min (limit, r.length)) _.forEach (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) return r,
Finalmente, ecco il graphql
endpoint, passando lo schema e l'oggetto valore root:
app.use ('/ graphql', graphqlHTTP (schema: schema, rootValue: rootValue, graphiql: true)); app.listen (3000); module.exports = app;
Ambientazione graphiql
a vero
ci consente di testare il server con un eccezionale IDE GraphQL nel browser. Lo consiglio vivamente per la sperimentazione di domande diverse.
Tutto è pronto. Passiamo a http: // localhost: 3000 / graphql e divertiamoci.
Possiamo iniziare in modo semplice, con solo una lista dei nomi dei giocatori:
query justNames allPlayers name Output: "data": "allPlayers": ["nome": "Stephen Curry", "nome": "Michael Jordan", "nome": "Scottie Pippen ", " name ":" Magic Johnson ", " name ":" Kobe Bryant ", " name ":" Kevin Durant "]
Tutto apposto. Abbiamo alcune superstar qui. Nessun dubbio. Andiamo per qualcosa di più bello: a partire dall'offset 4 prendi 2 giocatori. Per ogni giocatore, restituisci il loro nome e quanti campionati hanno vinto, oltre al nome della squadra e al numero di campionati vinti dalla squadra.
query twoPlayers allPlayers (offset: 4, limite: 2) nome championshipCount team nome championshipCount Output: "data": "allPlayers": ["name": "Kobe Bryant", "championshipCount": 5, "team": "name": "Los Angeles Lakers", "championshipCount": 16, "name": "Kevin Durant", "championshipCount": 1, "team": "nome": "Golden State Warriors", "championshipCount": 5]
Quindi Kobe Bryant ha vinto cinque campionati con i Lakers, che hanno vinto 16 campionati nel complesso. Kevin Durant ha vinto un solo campionato con i Warriors, che hanno vinto cinque campionati in totale.
Magic Johnson era un mago sul campo di sicuro. Ma non avrebbe potuto farlo senza il suo amico Kareem Abdul-Jabbar. Aggiungiamo Kareem al nostro database. Possiamo definire le mutazioni GraphQL per eseguire operazioni come aggiungere, aggiornare e rimuovere dati dal nostro grafico.
Innanzitutto, aggiungiamo un tipo di mutazione allo schema. Sembra un po 'come una firma di funzione:
type Mutation createPlayer (name: String, championshipCount: Int, teamId: String): Player
Quindi, dobbiamo implementarlo e aggiungerlo al valore radice. L'implementazione prende semplicemente i parametri forniti dalla query e aggiunge un nuovo oggetto al dati [ 'allPlayers']
. Inoltre si accerta di impostare correttamente la squadra. Alla fine, restituisce il nuovo giocatore.
createPlayer: (args) => id = (_.values (data ['allPlayers']). length + 1) .toString () args ['id'] = id args ['team'] = dati ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args dati di ritorno [' allPlayers '] [id],
Per aggiungere effettivamente Kareem, possiamo richiamare la mutazione e interrogare il giocatore restituito:
mutation addKareem createPlayer (nome: "Kareem Abdul-Jabbar", championshipCount: 6, teamId: "2") nome championshipCount team name Output: "data": "createPlayer": "nome": "Kareem Abdul-Jabbar", "championshipCount": 6, "team": "nome": "Los Angeles Lakers"
Ecco un piccolo segreto oscuro sulle mutazioni ... sono in realtà esattamente le stesse query. Puoi modificare i tuoi dati in una query e potresti semplicemente restituire i dati da una mutazione. GraphQL non ha intenzione di sbirciare nel tuo codice. Entrambe le query e le mutazioni possono assumere argomenti e restituire dati. È più simile allo zucchero sintattico per rendere il tuo schema più leggibile.
Le sottoscrizioni sono un'altra caratteristica killer di GraphQL. Con gli abbonamenti, il cliente può iscriversi agli eventi che verranno attivati ogni volta che le modifiche dello stato del server. Le sottoscrizioni sono state introdotte in una fase successiva e sono implementate da diversi framework in diversi modi.
GraphQL verificherà ogni query o mutazione rispetto allo schema. Questa è una grande vittoria quando i dati di input hanno una forma complessa. Non devi scrivere codice di convalida fastidioso e fragile. GraphQL si prenderà cura di te per te.
È possibile ispezionare e interrogare lo schema corrente stesso. Questo ti dà i meta-poteri per scoprire dinamicamente lo schema. Ecco una query che restituisce tutti i nomi dei tipi e la loro descrizione:
query q __schema types name description
GraphQL è una nuova entusiasmante tecnologia API che offre numerosi vantaggi rispetto alle API REST. C'è una vibrante comunità dietro di esso, per non parlare di Facebook. Prevedo che diventerà un punto fermo in un batter d'occhio. Provaci. Ti piacerà.