GraphQL - Cache

Cache é o processo de armazenamento de dados em uma área de armazenamento temporário chamada cache. Quando você retorna a uma página que visitou recentemente, o navegador pode obter esses arquivos do cache em vez do servidor original. Isso economiza seu tempo e rede da carga de tráfego adicional.

Os aplicativos cliente que interagem com o GraphQL são responsáveis ​​pelo armazenamento em cache dos dados em sua extremidade. Um padrão possível para isso é reservar um campo, como id, para ser um identificador globalmente exclusivo.

Cache InMemory

InMemoryCache é um armazenamento de dados normalizado comumente usado em aplicativos cliente GraphQL sem o uso de outra biblioteca como Redux.

O código de amostra para usar InMemoryCache com ApolloClient é fornecido abaixo -

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

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

O construtor InMemoryCache usa um objeto de configuração opcional com propriedades para personalizar seu cache.

Sr. Não. Parâmetro e Descrição
1

addTypename

Um booleano para determinar se deve ser adicionado __typename ao documento (padrão: verdadeiro)

2

dataIdFromObject

Uma função que pega um objeto de dados e retorna um identificador único para ser usado ao normalizar os dados na loja

3

fragmentMatcher

Por padrão, o InMemoryCache usa um matcher de fragmento heurístico

4

cacheRedirects

Um mapa de funções para redirecionar uma consulta para outra entrada no cache antes que uma solicitação ocorra.

Ilustração

Criaremos um aplicativo de página única no ReactJS com duas guias - uma para a guia inicial e outra para alunos. A guia de alunos carregará dados de uma API do servidor GraphQL. O aplicativo consultará os dados dos alunos quando o usuário navegar da guia inicial para a guia dos alunos. Os dados resultantes serão armazenados em cache pelo aplicativo.

Também consultaremos o tempo do servidor usando getTimecampo para verificar se a página está em cache. Se os dados forem retornados do cache, a página exibirá a hora da primeira solicitação enviada ao servidor. Se os dados forem resultado de uma nova solicitação feita ao servidor, ele sempre mostrará a hora mais recente do servidor.

Configurando o Servidor

A seguir estão as etapas para configurar o servidor -

Etapa 1 - Baixe e instale as dependências necessárias para o projeto

Crie uma pasta cache-server-app. Mude seu diretório para cache-server-app do terminal. Siga as etapas 3 a 5 explicadas no capítulo Configuração do ambiente.

Etapa 2 - Criar um esquema

Adicionar schema.graphql arquivo na pasta do projeto cache-server-app e adicione o seguinte código -

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

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

Etapa 3 - Adicionar resolvedores

Crie um arquivo resolvers.js na pasta do projeto e adicione o seguinte código -

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}

Etapa 4 - Executar o aplicativo

Crie um arquivo server.js. Consulte a etapa 8 no capítulo Configuração de ambiente. Execute o comando npm start no terminal. O servidor estará instalado e funcionando na porta 9000. Aqui, usaremos GraphiQL como um cliente para testar o aplicativo.

Abra o navegador e digite o URL http://localhost:9000/graphiql. Digite a seguinte consulta no editor -

{
   getTime
   students {
      id
      firstName
   }
}

O exemplo de resposta mostra os nomes dos alunos e o horário do servidor.

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

Configurando o ReactJS Client

Abra um novo terminal para o cliente. O terminal do servidor deve ser mantido em execução antes de executar o aplicativo cliente. O aplicativo React será executado na porta número 3000 e o aplicativo do servidor na porta número 9000.

Etapa 1 - Criar um aplicativo React

No terminal do cliente, digite o seguinte comando -

npx create-react-app hello-world-client

Isso instalará tudo o que é necessário para um aplicativo típico de reação. onpx utility e create-react-appferramentas criam um projeto com o nome hello-world-client. Assim que a instalação for concluída, abra o projeto no VSCode.

Instale os módulos do roteador para reagir usando o seguinte comando - npm install react-router-dom.

Etapa 2 - Iniciar o cliente hello-world

Altere o caminho da pasta atual no terminal para hello-world-client. Digite npm start para iniciar o projeto. Isso executará um servidor de desenvolvimento na porta 3000, abrirá automaticamente o navegador e carregará a página de índice.

Isso é mostrado na imagem abaixo -

Etapa 3 - Instale as bibliotecas cliente Apollo

Para instalar um cliente Apollo, abra um novo terminal e esteja no caminho da pasta do projeto atual. Digite o seguinte comando -

npm install apollo-boost graphql

Isso fará o download das bibliotecas Graphql para o lado do cliente e também do pacote Apollo Boost. Podemos verificar isso digitando npm view apollo-boost dependencies. Isso terá muitas dependências, conforme mostrado abaixo -

{ 
   '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' 
}

Podemos ver claramente que a biblioteca cliente-apollo está instalada.

Etapa 4 - Modificar o componente do aplicativo no arquivo index.js

Para uma aplicação simples de reação, você só precisa manter o index.js dentro src pasta e index.htmlem pasta pública; todos os outros arquivos gerados automaticamente podem ser removidos.

A estrutura do diretório é fornecida abaixo -

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

Adicione um arquivo adicional students.js que conterá o componente Students. Os detalhes do aluno são obtidos por meio do Componente do aluno. No componente de aplicativo, estamos usando um HashRouter.

A seguir está o index.js na aplicação de reação -

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"))

Etapa 5 - Editar Alunos de Componente em Students.js

No Componente de alunos, usaremos as duas abordagens a seguir para carregar dados -

  • Fetch API (loadStudents_noCache) - Isso irá disparar uma nova solicitação toda vez que clicar na guia do aluno.

  • Apollo Client (loadWithApolloclient) - Isso irá buscar dados do cache.

Adicionar uma função loadWithApolloclientquais consultas para alunos e tempo do servidor. Esta função habilitará o cache. Aqui, usamos uma função gql para analisar a consulta.

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

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

o Fetch APIé uma interface simples para buscar recursos. Fetch torna mais fácil fazer solicitações da web e lidar com respostas do que com o XMLHttpRequest mais antigo. O método a seguir mostra o carregamento de dados diretamente usando 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;
}

No construtor de StudentsComponent, chame o loadWithApolloClientmétodo. O completo Student.js o arquivo está abaixo -

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

Etapa 6 - Executar o aplicativo React com npm start

Você pode testar o aplicativo Rea, alternando da guia inicial para a guia alunos. Assim que a guia de alunos for carregada com os dados do servidor. Ele armazenará os dados em cache. Você pode testá-lo alternando da guia casa para a guia de alunos várias vezes. A saída será como mostrado abaixo -

Se você carregou a página dos alunos primeiro digitando o URL, http://localhost:3000/#/students, você pode ver que o tempo de carregamento para o app react e GraphQL seria aproximadamente o mesmo. Depois disso, se você alternar para a visualização inicial e retornar ao servidor GraphQL, a hora não mudará. Isso mostra que os dados estão armazenados em cache.

Etapa 7 - Alterar a chamada loadWithApolloclient para loadStudents_noCache

Se você alterar o método de carregamento para loadStudents_noCacheno construtor de StudentComponent, a saída não armazenará os dados em cache. Isso mostra a diferença entre armazenamento em cache e não armazenamento em cache.

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

A partir da saída acima, fica claro que se você alternar entre as guias, o tempo do servidor graphql será sempre o mais recente, o que significa que os dados não são armazenados em cache.