Automatizando a criação de hooks e funções para chamadas server e client side no seu frontend
Arthur 15 de junho de 2022 - Leia em 12 minutos
Hoje em dia o GraphQL se tornou uma ferramenta que gera uma flexibilidade gigantesca na criação de rotas de APIs (justamente por não as ter), o que deixa ainda mais performático com outras ferramentas que criam toda a lógica do GraphQL para você, tornando a criação de um backend para MVPs extremamente simples e rápida. Vamos explorar como tornar o nosso frontend tão performático quanto, automatizando toda a geração de hooks e funções para chamadas server e client side.
Setup projeto em Next
Primeiramente, criaremos um projeto em Next, pois o intuito é automatizarmos tanto as lógicas de chamas client side quanto server side, e para isso, executamos o comando abaixo:
yarn create next-app graphql-next --typescript
Agora, vamos instalar tanto o GraphQL, quanto o Apollo Client na nossa aplicação:
yarn add graphql@15 @apollo/client
Instalamos o GraphQL na versão 15, pois o Apollo e o Codegen ainda não suportam as versões mais novas.
Nos meus projetos next, costumo colocar todas as pastas dentro da pasta src, com exceção da pasta public. Dentro da nossa src, vamos criar uma pasta "lib", onde criaremos um arquivo "apollo.ts". Nesse arquivo, vamos criar as configurações do client do Apollo:
// src/lib/apollo.ts
import { ApolloClient, createHttpLink, from, InMemoryCache } from "@apollo/client"
link: from([httpLink]),
Como exemplo, utilizaremos a API do Rick e Morty, que pode ser acessada pelo link abaixo:
Playground – https://rickandmortyapi.com/graphql
Para que o Apollo funcione em qualquer lugar da nossa aplicação, precisamos colocar o nosso ApolloProvider no nosso _app.tsx, e passar o apolloClient que acabamos de criar, da seguinte forma:
import { apolloClient } from '../lib/apollo'
Agora, dentro da pasta pages, vamos criar uma query no nosso index.tsx apenas para testar a implementação do Apollo:
const { data } = useQuery(GET_CHARACTERS)
Note que no modelo tradicional do Apollo, utilizamos o hook useQuery passando a nossa query. A ideia é que de acordo com schema do nosso GraphQL, todos os hooks com cada uma dessas queries sejam gerados automaticamente, tanto para serem utilizadas no client side quanto no server side.
Dessa forma teremos o seguinte retorno na nossa página inicial:
Chegou a hora de ver a mágica acontecer! Como falamos anteriormente, o Codegen criará hooks usando o useQuery que utilizamos anteriormente. Mas, já passando todas as queries prontas e, o melhor de tudo, tipadas!
Para isso, vamos instalar alguns pacotes:
yarn add @graphql-codegen/cli @graphql-codegen/import-types-preset @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo graphql-codegen-apollo-next-ssr -D
Sim, são vários pacotes, mas cada um tem seu papel para fazer toda a implementação com Typescript, Next e SSR.
Agora, vamos criar o seguinte arquivo yml na raiz do nosso projeto, que é o arquivo que recebe todas as configurações do codegen:
schema: '<https://rickandmortyapi.com/graphql>'
documents: './src/graphql/**/*.graphql'
src/graphql/generated/graphql.tsx:
documentMode: graphQLTag
src/graphql/generated/page.tsx:
importDocumentNodeExternallyFrom: ./graphql
contextType: 'ApolloClientContext'
contextTypeRequired: true
apolloClientInstanceImport: '../../lib/withApollo'
typesPath: ./graphql
- 'graphql-codegen-apollo-next-ssr'
Esse arquivo basicamente configura todos os plugins que instalamos anteriormente, indica onde estão as queries que iremos criar, onde está o nosso cliente e qual o endereço do nosso schema graphql.
Vamos criar um script no nosso package.json para rodar esse arquivo:
E vamos recriar nossa query, agora separada em um arquivo ".graphql":
// src/graphql/queries/**characters.graphql**
Por fim, executamos o nosso script para gerar os nossos hooks, funções e toda a tipagem:
Então será gerado as seguintes pastas e arquivos no seu projeto:
Agora vamos testar nossos novos hooks. Vamos voltar ao nosso index.tsx, deletar nossa query e o useQuery, e utilizar o nosso useGetCharactersQuery:
import { useGetCharactersQuery } from '../graphql/generated/graphql'
const { data } = useGetCharactersQuery()
Se tudo estiver funcionando, você verá que nada mudou e que a nossa query continua funcionando normalmente. Vale ressaltar que agora o nosso data está todo tipado e já sabemos exatamente a estrutura que virá do nossa query.
Para utilizar as nossas queries no lado do servidor, vamos precisar fazer algumas alterações no nosso client do Apollo e transformar nosso provider em um HOC (High Order Component). Ou seja, iremos criar um componente que recebe outro componente e encapsula esse componente no nosso ApolloProvider.
Basicamente, transformaremos o nosso apolloClient nesse HOC e renomearemos para withApollo:
// src/lib/withApollo.tsx
import {  GetServerSidePropsContext, NextPage } from 'next'
export const withApollo = (Component: NextPage) => {
return function Provider(props: any) {
<ApolloProvider client={getApolloClient(undefined, props.apolloState)}>
export type ApolloClientContext = GetServerSidePropsContext
export function getApolloClient(
ctx?: ApolloClientContext,
ssrCache?: NormalizedCacheObject
const cache = new InMemoryCache().restore(ssrCache ?? {})
Se você notar, vai ver que existem alguns detalhes a mais na implementação do withApollo, como um restore do cache e um contexto (ctx) que não é usado em lugar nenhum. Mas, isso são parâmetros que o próprio Apollo e o Codegen usam nas suas implementações. Sem o restore do cache, o SSR não funciona, pois o Apollo armazena as queries SSR nesse cache. E, sem esse ctx, o Codegen também implementa suas funções errado, já que nas funções ele utiliza tanto esse ctx, quanto a exportação que fizemos do tipo "ApolloClientContext".
Agora, vamos remover o ApolloProvider do _app.tsx:
E no nosso index, vamos encapsular a exportação do nosso componente com o withApollo:
import { withApollo } from '../lib/withApollo'
export default withApollo(Home)
Tudo estará funcionando normalmente.
Realizando nossa chamada SSR
Para realizarmos a nossa query pelo lado do servidor, basta apenas fazermos mais um encapsulamento no nosso HOC do withApollo e depois chamar uma função que gera as nossas props do SSR já com o nosso data:
import { GetCharactersQuery } from '../graphql/generated/graphql'
import { getServerPageGetCharacters, ssrGetCharacters } from '../graphql/generated/page'
data: GetCharactersQuery
const Home: NextPage<HomeProps> = ({ data }) => {
return getServerPageGetCharacters({}, ctx)
export default withApollo(ssrGetCharacters.withPage()(Home))
Assim, vamos receber dentro das nossas props o data com resultado da nossa query.
Dessa forma, basta apenas escrever as nossas queries uma por uma e colocar na nossa pasta, gerar todos os hooks e funções com o codegen, e ir utilizando tanto no client side, quanto no server side da nossa aplicação. Esse setup facilita bastante o desenvolvimento, principalmente porque não temos que ficar criando tipos e importando queries pra lá e pra cá, já que o codegen faz todo esse trabalho para nós.