Indice dei contenuti

Vantaggi dell'utilizzo di GraphQL rispetto alle API REST

Uno dei problemi più comuni di REST è l'eccesso e il difetto di recupero dei dati. Ciò accade perché l'unico modo in cui un client può scaricare i dati è colpire endpoint che restituiscono strutture di dati fisse. È difficile progettare l'API in modo che possa fornire ai clienti le loro esatte esigenze di dati.

  • GraphQL riduce le richieste di rete consentendoci di recuperare tutti i dati di cui abbiamo bisogno con un'unica query.
  • Con GraphQL, non c'è bisogno di un versioning, perché possiamo facilmente aggiungere nuovi campi e tipi alla nostra API GraphQL senza impattare sulle query esistenti. Inoltre, possiamo facilmente contrassegnare i campi come deprecati e questi saranno esclusi dalla risposta ricevuta dal server.
  • Con GraphQL, è possibile anche effettuare un monitoraggio di basso livello delle prestazioni delle richieste elaborate dal server. GraphQL utilizza il concetto di funzioni resolver per raccogliere i dati richiesti da un client. La strumentazione e la misurazione delle prestazioni di questi resolver forniscono informazioni cruciali sui colli di bottiglia del sistema.

Breve spiegazione del funzionamento di GraphQL

L'implementazione di GraphQL contiene principalmente tre parti

  • Schema - Descrive i dati.
  • Risolutori - Logica per recuperare i dati da diverse risorse (microservizi).
  • Query - Il client chiede quali dati devono essere recuperati.

Schema(Ulteriori informazioni su Schema e tipi)

➡️
type Query{getUsers:[User]getUser(user_id:Int!):User}type Mutation{addUser(first_name:String!,last_name:String,pic:String):Boolean deleteUser(user_id:Int!):Boolean}type User{first_name:Stringlast_name:Stringpic:Stringfull_name:Stringuser_id:Int!designation:String}

Il codice precedente descrive ciò che può essere interrogato e i dati di risposta (tipo di dati) attesi dalle risorse.

P.S.: "!" significa che il campo non può essere nullo o indefinito.

Risolutori (il codice sottostante è scritto in NodeJs)

➡️
{Query:{getUsers:(obj,args,context,info) => { return users},getUser:(obj,{user_id},context,info) => { return users.find(user => user.user_id == user_id) } },Mutation:{ addUser:(obj,{first_name,last_name,pic},context,info) => { users.push({ first_name:first_name, last_name:last_name, pic:pic }) return true },deleteUser:(obj,{user_id},context,info) => { for (var i = 0; i < users.length; i++) { if(users[i].user_id == user_id){ users.splice(i,1) return true }}return false } }, User:{ full_name:(prev_obj,args,context,info) => { return `${prev_obj.first_name} ${prev_obj.last_name}`}}}

Il codice sopra descritto descrive la logica che viene eseguita quando il client effettua una richiesta.

P.S. Ho usato la funzione resolver full_name per il tipo User per scrivere la mia logica.

Query(Ulteriori informazioni sulla query)

➡️
{getUsers{full_name pic}}

Ilclient esegue la query utilizzando il codice sopra riportato che restituisce solo nome_completo e foto come di seguito.

P.S. Il client può interrogare solo i campi definiti nello schema.

➡️
{"data":{"getUsers":{"full_name":"Srinivasa Sainath", "pic":"https://vpms.xoxoday.com/images/xoxoday.png"}}}

La richiesta del client può essere di tipo Query e Mutation (e Subscription. Se ne parlerà nei prossimi blog della serie GraphQL).

Entrambe le richieste fanno la stessa cosa. Solo che la mutazione viene eseguita in modo sincrono. La convenzione che viene seguita è che la Mutazione viene utilizzata per qualsiasi operazione che provoca una scrittura nel server (aggiornamento del profilo, creazione di un ordine, ecc.) e una query viene utilizzata per recuperare i dati (ottenere il menu, ottenere l'elenco dei buoni, ecc.). (simile a GET e POST). Tecnicamente, entrambi i tipi di richiesta possono essere utilizzati per eseguire qualsiasi logica. Ma è meglio seguire le convenzioni, perché aiutano il team.

Punti importanti per comprendere la relazione tra le funzioni del resolver e la query

  • Si può pensare a ciascun campo di una query GraphQL come a una funzione o a un metodo del tipo precedente che restituisce il tipo successivo. Questo è esattamente il modo in cui GraphQL funziona. Ogni campo di ogni tipo è supportato da una funzione chiamata resolver, fornita dallo sviluppatore del server GraphQL. Il resolver corrispondente viene chiamato per produrre il valore successivo quando un campo viene eseguito.
  • L'esecuzione viene completata se un campo produce un valore scalare, come una stringa o un numero. Tuttavia, se un campo ha un valore di oggetto, la query conterrà un'altra selezione di campi che si applicano a quell'oggetto. L'esecuzione continua fino a quando non vengono raggiunti i valori scalari. Le query GraphQL terminano sempre con valori scalari.

Una funzione resolver riceve quattro argomenti

Sintassi :

➡️
{getUsers:(obj,args,context,info) =>{ return users }}
  • obj L'oggetto precedente, che per un campo del tipo di Query principale spesso non viene utilizzato.
    Per capire meglio questo argomento, fare riferimento a Server GraphQL con Node.js.
  • args Gli argomenti forniti al campo nella query GraphQL. Nella funzione resolver di cui sopra {user_input} è l'args
  • context Un valore che viene fornito a ogni resolver e che contiene importanti informazioni contestuali, come l'utente attualmente connesso o l'accesso a un database. Nel resolver di cui sopra, la funzione context è usata per passare token_info
  • info Valore che contiene informazioni specifiche sul campo, rilevanti per la query corrente e per i dettagli dello schema. Spesso non utilizzato
➡️
Clone the demo GraphQL project from here.P.S. In the next part of the GraphQL series, I will discuss how to implement GraphQL as an API Gateway.
Srinivasa Sainath

Srinivasa Sainath