GraphQL - Caching

La memorizzazione nella cache è il processo di archiviazione dei dati in un'area di archiviazione temporanea chiamata cache. Quando torni a una pagina che hai visitato di recente, il browser può ottenere quei file dalla cache anziché dal server originale. Ciò consente di risparmiare tempo e rete dal carico di traffico aggiuntivo.

Le applicazioni client che interagiscono con GraphQL sono responsabili della memorizzazione nella cache dei dati alla loro estremità. Un possibile modello per questo è riservare un campo, come id, come identificatore univoco globale.

InMemory Cache

InMemoryCache è un archivio dati normalizzato comunemente utilizzato nelle applicazioni client GraphQL senza l'uso di altre librerie come Redux.

Il codice di esempio per utilizzare InMemoryCache con ApolloClient è fornito di seguito:

import {ApolloClient, HttpLink, InMemoryCache} from 'apollo-boost'
const cache = new InMemoryCache();

const client = new ApolloClient({
   link: new HttpLink(),
   cache
});

Il costruttore InMemoryCache accetta un oggetto di configurazione opzionale con proprietà per personalizzare la cache.

Sr.No. Parametro e descrizione
1

addTypename

Un valore booleano per determinare se aggiungere __typename al documento (default: true)

2

dataIdFromObject

Una funzione che accetta un oggetto dati e restituisce un identificatore univoco da utilizzare durante la normalizzazione dei dati nell'archivio

3

fragmentMatcher

Per impostazione predefinita, InMemoryCache utilizza un abbinamento di frammenti euristico

4

cacheRedirects

Una mappa di funzioni per reindirizzare una query a un'altra voce nella cache prima che venga eseguita una richiesta.

Illustrazione

Creeremo un'applicazione a pagina singola in ReactJS con due schede: una per la scheda home e un'altra per gli studenti. La scheda Studenti caricherà i dati da un'API del server GraphQL. L'applicazione interrogherà i dati degli studenti quando l'utente passa dalla scheda Home alla scheda Studenti. I dati risultanti verranno memorizzati nella cache dall'applicazione.

Interrogeremo anche l'ora del server utilizzando getTimecampo per verificare se la pagina è memorizzata nella cache. Se i dati vengono restituiti dalla cache, la pagina visualizzerà l'ora della prima richiesta inviata al server. Se i dati sono il risultato di una nuova richiesta fatta al server, mostrerà sempre l'ora più recente dal server.

Configurazione del server

Di seguito sono riportati i passaggi per configurare il server:

Passaggio 1: scaricare e installare le dipendenze richieste per il progetto

Crea una cartella cache-server-app. Cambia la tua directory in cache-server-app dal terminale. Seguire i passaggi da 3 a 5 spiegati nel capitolo Configurazione dell'ambiente.

Passaggio 2: creare uno schema

Inserisci schema.graphql file nella cartella del progetto cache-server-app e aggiungi il seguente codice -

type Query {
   students:[Student]
   getTime:String
}

type Student {
   id:ID!
   firstName:String
   lastName:String
   fullName:String
}

Passaggio 3: aggiungere i resolver

Crea un file resolvers.js nella cartella del progetto e aggiungi il codice seguente:

const db = require('./db')

const Query = {
      students:() => db.students.list(),
      getTime:() => {
      const today = new Date();
      var h = today.getHours();
      var m = today.getMinutes();
      var s = today.getSeconds();
      return `${h}:${m}:${s}`;
   }
}
module.exports = {Query}

Passaggio 4: eseguire l'applicazione

Crea un file server.js. Fare riferimento al passaggio 8 nel capitolo Configurazione dell'ambiente. Esegui il comando npm start nel terminale. Il server sarà attivo e funzionante sulla porta 9000. Qui useremo GraphiQL come client per testare l'applicazione.

Apri il browser e inserisci l'URL http://localhost:9000/graphiql. Digita la seguente query nell'editor:

{
   getTime
   students {
      id
      firstName
   }
}

La risposta di esempio mostra i nomi degli studenti e l'ora del server.

{
   "data": {
      "getTime": "22:18:42",
      "students": [
         {
            "id": "S1001",
            "firstName": "Mohtashim"
         },
         {
            "id": "S1002",
            "firstName": "Kannan"
         },
         {
            "id": "S1003",
            "firstName": "Kiran"
         }
      ]
   }
}

Configurazione del client ReactJS

Apri un nuovo terminale per il cliente. Il terminale del server deve essere mantenuto in esecuzione prima di eseguire l'applicazione client. L'applicazione React verrà eseguita sulla porta numero 3000 e l'applicazione server sulla porta numero 9000.

Passaggio 1: creare un'applicazione React

Nel terminale del client, digita il seguente comando:

npx create-react-app hello-world-client

Questo installerà tutto il necessario per una tipica applicazione React. Ilnpx utility e create-react-apptools crea un progetto con il nome hello-world-client. Una volta completata l'installazione, apri il progetto in VSCode.

Installa i moduli del router per reagire usando il seguente comando: npm install react-router-dom.

Passaggio 2: avvia hello-world-client

Cambia il percorso della cartella corrente nel terminale in hello-world-client. Digita npm start per avviare il progetto. Questo eseguirà un server di sviluppo sulla porta 3000 e aprirà automaticamente il browser e caricherà la pagina dell'indice.

Questo è mostrato nella schermata riportata di seguito:

Passaggio 3: installare le librerie client di Apollo

Per installare un client Apollo, aprire un nuovo terminale e trovarsi nel percorso della cartella del progetto corrente. Digita il seguente comando:

npm install apollo-boost graphql

Questo scaricherà le librerie graphql per il lato client e anche il pacchetto Apollo Boost. Possiamo effettuare una verifica incrociata digitando npm view apollo-boost dependencies. Questo avrà molte dipendenze come mostrato di seguito -

{ 
   'apollo-cache': '^1.1.15',
   'apollo-cache-inmemory': '^1.2.8',
   'apollo-client': '^2.4.0',
   'apollo-link': '^1.0.6',
   'apollo-link-error': '^1.0.3',
   'apollo-link-http': '^1.3.1',
   'apollo-link-state': '^0.4.0',
   'graphql-tag': '^2.4.2' 
}

Possiamo vedere chiaramente che la libreria apollo-client è installata.

Passaggio 4: modificare il componente dell'app nel file index.js

Per una semplice applicazione React, devi solo mantenere il file index.js in src cartella e index.htmlnella cartella pubblica; tutti gli altri file generati automaticamente possono essere rimossi.

Di seguito viene fornita la struttura delle directory:

hello-world-client /
   -->node_modules
   -->public
      index.html
   -->src
      index.js
      students.js
   -->package.json

Aggiungi un file aggiuntivo students.js che conterrà il Componente Studenti. I dettagli dello studente vengono recuperati tramite il componente Student. Nel componente dell'app, stiamo usando un HashRouter.

Di seguito è riportato il file index.js nell'applicazione React -

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {HashRouter, Route, Link} from 'react-router-dom'

//components
import Students from './students'
class App extends Component {
   render() {
      return(
         <div><h1>Home !!</h1>
         <h2>Welcome to React Application !! </h2>
         </div>
      )
   }
}

function getTime() {
   var d = new Date();
   return d.getHours()+":"+d.getMinutes()+":"+d.getSeconds()
}

const routes = <HashRouter>
   <div>
      <h4>Time from react app:{getTime()}</h4>
      <header>
         <h1>  <Link to="/">Home</Link> 
         <Link to = "/students">Students</Link>  </h1>
      </header>
      <Route exact path = "/students" component = {Students}></Route>
      <Route exact path = "/" component = {App}></Route>
   </div>
</HashRouter>

ReactDOM.render(routes, document.querySelector("#root"))

Passaggio 5: modifica degli studenti del componente in Students.js

In Students Component, utilizzeremo i seguenti due approcci per caricare i dati:

  • Fetch API (loadStudents_noCache) - Questo attiverà una nuova richiesta ogni volta che fa clic sulla scheda Studente.

  • Apollo Client (loadWithApolloclient) - Questo recupererà i dati dalla cache.

Aggiungi una funzione loadWithApolloclientquali query per studenti e tempo dal server. Questa funzione abiliterà la memorizzazione nella cache. Qui usiamo una funzione gql per analizzare la query.

async loadWithApolloclient() {
   const query = gql`{
      getTime
      students {
         id
         firstName
      }
   }`;

   const {data} = await  client.query({query})
   return data;
}

Il Fetch APIè una semplice interfaccia per il recupero delle risorse. Fetch rende più facile effettuare richieste web e gestire le risposte rispetto al vecchio XMLHttpRequest. Il seguente metodo mostra il caricamento dei dati direttamente utilizzando fetch api -

async  loadStudents_noCache() {
      const response = await fetch('http://localhost:9000/graphql', {
      method:'POST',
      headers:{'content-type':'application/json'},
      body:JSON.stringify({query:`{
         getTime
         students {
            id
            firstName
         }
      }`})
   })

   const rsponseBody = await response.json();
   return rsponseBody.data;
}

Nel costruttore di StudentsComponent, chiama il file loadWithApolloClientmetodo. Il completo Student.js il file è sotto -

import React, {Component} from 'react';
import { Link} from 'react-router-dom'

//Apollo Client
import {ApolloClient, HttpLink, InMemoryCache} from 'apollo-boost'
import gql from 'graphql-tag'
const client = new ApolloClient({
   link: new HttpLink({uri:`http://localhost:9000/graphql`}),
   cache:new InMemoryCache()
})

class Students extends Component {
   constructor(props) {
      super(props);
      this.state = {
         students:[{id:1,firstName:'test'}],
         serverTime:''
      }
      this.loadWithApolloclient().then(data => {
         this.setState({
            students:data.students,
            serverTime:data.getTime
         })
      })
   }
   
   async  loadStudents_noCache() {
      const response = await fetch('http://localhost:9000/graphql', {
         method:'POST',
         headers:{'content-type':'application/json'},
         body:JSON.stringify({query:`{
            getTime
            students {
               id
               firstName
            }
         }`})
      })
      const rsponseBody =  await response.json();
      return rsponseBody.data;
   }
   
   async loadWithApolloclient() {
      console.log("inside apollo client function")
      const query = gql`{
         getTime
         students {
            id
            firstName
         }
      }`;
      const {data} = await  client.query({query})
      return data;
   }
   
   render() {
      return(
         <div>
            <h3>Time from GraphQL server :{this.state.serverTime}</h3>
            <p>Following Students Found </p>
            <div>
               <ul>
                  {
                     this.state.students.map(s => {
                        return(
                           <li key = {s.id}>
                              {s.firstName}
                           </li>
                        )
                     })
                  }
               </ul>
            </div>
         </div>
      )
   }
}
export default Students

Passaggio 6: eseguire l'applicazione React con npm start

Puoi testare l'applicazione React passando dalla scheda Home alla scheda Studenti. Una volta caricata la scheda Studenti con i dati dal server. Memorizzerà i dati nella cache. Puoi testarlo passando più volte dalla scheda Home alla scheda Studenti. L'output sarà come mostrato di seguito -

Se hai caricato prima la pagina degli studenti digitando l'URL, http://localhost:3000/#/students, puoi vedere che il tempo di caricamento per l'app React e GraphQL sarebbe approssimativamente lo stesso. Dopodiché, se si passa alla visualizzazione iniziale e si torna al server GraphQL, l'ora non cambierà. Ciò mostra che i dati sono memorizzati nella cache.

Passaggio 7: modificare la chiamata loadWithApolloclient in loadStudents_noCache

Se modifichi il metodo di caricamento in loadStudents_noCachenel costruttore di StudentComponent, l'output non memorizzerà i dati nella cache. Questo mostra la differenza tra caching e non caching.

this.loadStudents_noCache().then(data => {
   this.setState({
      students:data.students,
      serverTime:data.getTime
   })
})

Dall'output di cui sopra, è chiaro che se si passa avanti e indietro tra le schede, l'ora dal server graphql sarà sempre l'ultima, il che significa che i dati non vengono memorizzati nella cache.