Web Dev Drops

Fullstack com Node.js, React e GraphQL  – 10: Apollo Client

avatar
Douglas Matoso
Atualizado em 05/12/2020
Leitura: 5 min.

Fala, pessoal! Nesta parte 10 da série  Fullstack com Node.js, React e GraphQL vamos finalmente integrar o backend e o frontend, usando o Apollo Client para buscar os dados no servidor e fornecê-los para os componentes React.

Fullstack com Node.js, React e GraphQL  – 10: Apollo Client

Integração da autenticação

Antes de começar a usar o Apollo Client precisamos integrar a autenticação no front e back.

Hoje, no ambiente de desenvolvimento, temos que dar npm start em duas aplicações: o frontend e backend. Cada uma roda em uma porta (backend em 5000 e frontend em 5001).

Rodando tudo na porta 5000

Vamos adicionar um http proxy no backend para que o frontend fique disponível em http://localhost:5000/app.

Instalamos no backend a dependência:

npm i http-proxy-middleware

E habilitamos na rota /app:

// proxy
app.use(
  '/app',
  authMiddleware({ redirect: true }),
  createProxyMiddleware({ target: 'http://localhost:5001', changeOrigin: true })
)

Com essa configuração tudo que for acessado dentro da rota /app, internamente vai ser buscado em http://localhost:5001, todo o nosso frontend, incluindo os assets.

Veja que, antes do midleware de proxy, adicionamos o middleware de autenticação, portanto esta rota fica protegida. Se tentar acessá-la sem estar logado, ele redireciona para a raiz, onde vai ter a tela de login.

Tela de login

Adicionamos uma tela estática de login no backend, em public/login/index.html:

tela de login

O botão login manda para /auth/google, que dispara o fluxo de login pelo Google.

Rota raiz e logout

Por fim, atualizamos a rota raiz para redirecionar para /app se estiver logado ou /login, caso contrário:

app.get('/', (req, res) => {
  if (req.isAuthenticated()) {
    res.redirect('/app')
  } else {
    res.redirect('/login')
  }
})

Para sair da aplicação, basta ter um link para /auth/logout, que dispara o fluxo de logout.

Apollo Client

O Apollo Client é a parte do Apollo que fica no frontend. Ele vai enviar queries ao nosso servidor GraphQL e prover os dados para os componentes React.

Temos que instalar os pacotes no projeto de frontend:

npm install @apollo/client graphql

No index.ts fazemos a inicialização da lib, dizendo onde está o servidor GraphQL e como cachear os dados (por padrão a gente usa o cache em memória).

Devemos também adicionar o ApolloProvider em volta do componente App, para que toda a aplicação tenha acesso ao contexto do Apollo Client:

import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'

const client = new ApolloClient({
  uri: 'http://localhost:5000/graphql',
  cache: new InMemoryCache(),
})

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('app')
)

Fazendo as queries para buscar os dados

Com o Apollo Client configurado, podemos fazer queries nos nossos componentes para buscar dados no servidor.

Vamos começar com estes cards na home:

cards de patrimônio, rentabilidade e lucro

Primeiro a gente declara uma query fora do componente usando a função gql:

import { gql, useQuery } from '@apollo/client'

const balancesQuery = gql`
  query {
    investments {
      balance
      invested
    }
  }
`

Esta query busca todos os investimentos, trazendo o saldo atual e o total investido de cada um.

Já dentro do componente a gente usa o hook useQuery do Apollo para pegar os dados resultantes desta query:

const { loading, data, error } = useQuery<{
  investments: { balance: number; invested: number }[]
}>(balancesQuery)

Veja que o useQuery permite especificar o tipo de dado que a query deve retornar através do parâmetro de tipo { investments: { balance: number; invested: number }[]; }

O hook useQuery retorna três informações:
- loading: booleano indicando se a requisição com o servidor ainda está em andamento
- error: caso a requisição tenha retornado um erro
- data: resultado da query caso a requisição tenha ocorrido com sucesso

Com isso podemos tratar cada caso. No nosso caso, fizemos um tratamento bem simples do loading e error para ilustrar:

if (loading) {
  return <p>Carregando...</p>
}

if (error) {
  return <p>{error.toString()}</p>
}

Como o data vai trazer os valores por investimento, vamos somar tudo para exibir nos cards o patrimônio total e fazer os cálculos de lucro e rentabilidade.

A gente poderia fazer esse cálculo no backend, mas como não são complexos, vamos fazer no client mesmo.

// saldo total da carteira
const totalBalance =
  data?.investments.reduce((prev, cur) => prev + cur.balance, 0) || 0
// valor investido total
const totalInvested =
  data?.investments.reduce((prev, cur) => prev + cur.invested, 0) || 0
// lucro em R$
const profit = totalBalance - totalInvested
// rentabilidade em %
const profitMargin = profit / totalInvested

Note que usamos optional chaining - data?.investments - pois para o TypeScript data pode não estar definido. Como já tratamos os casos de loading e error, neste ponto podemos assumir que data terá algum valor.

Feito isso podemos exibir os valores em um componente Card que criamos:

<Card title="Patrimônio" value={totalBalance} />

Para o gráfico na home a ideia é a mesma:

gráfico de evolução dos investimentos

A query para isso vai trazer os nomes e atualizações de saldo dos investimentos:

const investmentsQuery = gql`
  query {
    investments {
      id
      name
      balanceUpdates(order: [["date", "ASC"]]) {
        date
        amount
      }
    }
  }
`

Por fim, a tabela com informações dos investimentos é bem simples:

tabela de investimentos

A query traz nome, saldo e valor investido de cada investimento, bem como o nome da corretora:

const investmentsQuery = gql`
  query {
    investments {
      id
      name
      broker {
        name
      }
      balance
      invested
    }
  }
`

Olha aí a beleza do GraphQL. Pedimos exatamente os dados que precisamos, inclusive dados relacionados, como o nome da corretora de cada investimento, e o backend retorna exatamente isso, nem mais, nem menos.

Resultado final

O código do projeto até este ponto está em: https://github.com/doug2k1/my-money/tree/v10.0.0

No próximo capítulo

Na próxima parte vamos adicionar testes no nosso frontend.

Stay tuned!

Comentários

Comentários desabilitados